mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-14 17:06:16 +00:00
minor improvements to new image support
restore isImageSupported code lost in merge more cleanup in ImageUtils and MediaViewImagePanel lazily instantiate static fileTypeDetector better notification of errorsl loading images special case to display iff images incorectly identifiead as audio/x-aiff show image icon in result viewers for more files
This commit is contained in:
parent
dc65afc62b
commit
5e375dd557
@ -206,7 +206,7 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -218,8 +218,8 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
||||
* @return True if an image file that can be displayed
|
||||
*/
|
||||
private boolean isImageSupported(AbstractFile file) {
|
||||
return ImageUtils.thumbnailSupported(file);
|
||||
|
||||
return ImageUtils.thumbnailSupported(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.label {
|
||||
-fx-wrap-text:true;
|
||||
-fx-text-fill: red;
|
||||
-fx-font-size: 2em;
|
||||
}
|
||||
|
||||
.bg {
|
||||
-fx-background-color:black;
|
||||
}
|
@ -32,14 +32,11 @@ import java.util.stream.Collectors;
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.Background;
|
||||
import javafx.scene.layout.BackgroundFill;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.CornerRadii;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
@ -67,6 +64,10 @@ public class MediaViewImagePanel extends JPanel {
|
||||
private ImageView fxImageView;
|
||||
private BorderPane borderpane;
|
||||
|
||||
private final Label errorLabel = new Label("Could not load image file into media view.");
|
||||
private final Label tooLargeLabel = new Label("Could not load image file into media view (too large).");
|
||||
private final Label noReaderLabel = new Label("Image reader not found for file.");
|
||||
|
||||
/**
|
||||
* mime types we shoul dbe able to display. if the mimetype is unknown we
|
||||
* will fall back on extension (and jpg/png header
|
||||
@ -79,7 +80,6 @@ public class MediaViewImagePanel extends JPanel {
|
||||
static private final List<String> supportedExtensions = ImageUtils.getSupportedExtensions().stream()
|
||||
.map("."::concat)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
||||
/**
|
||||
* Creates new form MediaViewImagePanel
|
||||
@ -88,12 +88,15 @@ public class MediaViewImagePanel extends JPanel {
|
||||
initComponents();
|
||||
fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited();
|
||||
if (fxInited) {
|
||||
Platform.runLater(() -> { // build jfx ui (we could do this in FXML?)
|
||||
Platform.runLater(() -> {
|
||||
|
||||
// build jfx ui (we could do this in FXML?)
|
||||
fxImageView = new ImageView(); // will hold image
|
||||
borderpane = new BorderPane(fxImageView); // centers and sizes imageview
|
||||
borderpane.setBackground(new Background(new BackgroundFill(javafx.scene.paint.Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
|
||||
borderpane.getStyleClass().add("bg");
|
||||
fxPanel = new JFXPanel(); // bridge jfx-swing
|
||||
Scene scene = new Scene(borderpane, javafx.scene.paint.Color.BLACK); //root of jfx tree
|
||||
Scene scene = new Scene(borderpane); //root of jfx tree
|
||||
scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm());
|
||||
fxPanel.setScene(scene);
|
||||
|
||||
//bind size of image to that of scene, while keeping proportions
|
||||
@ -120,6 +123,7 @@ public class MediaViewImagePanel extends JPanel {
|
||||
public void reset() {
|
||||
Platform.runLater(() -> {
|
||||
fxImageView.setImage(null);
|
||||
borderpane.setCenter(null);
|
||||
});
|
||||
}
|
||||
|
||||
@ -144,41 +148,38 @@ public class MediaViewImagePanel extends JPanel {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Case.isCaseOpen()) {
|
||||
//handle in-between condition when case is being closed
|
||||
//and an image was previously selected
|
||||
/* handle in-between condition when case is being closed
|
||||
* and an image was previously selected */
|
||||
return;
|
||||
}
|
||||
|
||||
final Image fxImage;
|
||||
|
||||
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(file));) {
|
||||
|
||||
BufferedImage bufferedImage = ImageIO.read(inputStream);
|
||||
if (bufferedImage == null) {
|
||||
LOGGER.log(Level.WARNING, "Could image reader not found for file: {0}", file.getName()); //NON-NLS
|
||||
return;
|
||||
LOGGER.log(Level.WARNING, "Image reader not found for file: {0}", file.getName()); //NON-NLS
|
||||
borderpane.setCenter(noReaderLabel);
|
||||
} else {
|
||||
Image fxImage = SwingFXUtils.toFXImage(bufferedImage, null);
|
||||
if (fxImage.isError()) {
|
||||
LOGGER.log(Level.WARNING, "Could not load image file into media view: " + file.getName(), fxImage.getException()); //NON-NLS
|
||||
borderpane.setCenter(errorLabel);
|
||||
return;
|
||||
} else {
|
||||
fxImageView.setImage(fxImage);
|
||||
borderpane.setCenter(fxImageView);
|
||||
}
|
||||
}
|
||||
fxImage = SwingFXUtils.toFXImage(bufferedImage, null);
|
||||
} catch (IllegalArgumentException | IOException ex) {
|
||||
LOGGER.log(Level.WARNING, "Could not load image file into media view: " + file.getName(), ex); //NON-NLS
|
||||
|
||||
return;
|
||||
borderpane.setCenter(errorLabel);
|
||||
} catch (OutOfMemoryError ex) {
|
||||
LOGGER.log(Level.WARNING, "Could not load image file into media view (too large): " + file.getName(), ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.warn(
|
||||
NbBundle.getMessage(this.getClass(), "MediaViewImagePanel.imgFileTooLarge.msg", file.getName()),
|
||||
ex.getMessage());
|
||||
return;
|
||||
borderpane.setCenter(tooLargeLabel);
|
||||
}
|
||||
|
||||
if (fxImage.isError()) {
|
||||
|
||||
LOGGER.log(Level.WARNING, "Could not load image file into media view: " + file.getName(), fxImage.getException()); //NON-NLS
|
||||
return;
|
||||
}
|
||||
fxImageView.setImage(fxImage);
|
||||
borderpane.setCenter(fxImageView);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
//show the panel after fully loaded
|
||||
fxPanel.setVisible(true);
|
||||
|
@ -25,7 +25,7 @@ package org.sleuthkit.autopsy.coreutils;
|
||||
import com.google.common.io.Files;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -49,6 +49,7 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector.FileTypeDetectorInitException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
@ -63,20 +64,16 @@ public class ImageUtils {
|
||||
private static final Logger LOGGER = Logger.getLogger(ImageUtils.class.getName());
|
||||
|
||||
/** save thumbnails to disk as this format */
|
||||
private static final String FORMAT = "png";
|
||||
private static final String FORMAT = "png"; //NON-NLS
|
||||
|
||||
public static final int ICON_SIZE_SMALL = 50;
|
||||
public static final int ICON_SIZE_MEDIUM = 100;
|
||||
public static final int ICON_SIZE_LARGE = 200;
|
||||
|
||||
private static final Image DEFAULT_ICON = new ImageIcon("/org/sleuthkit/autopsy/images/file-icon.png").getImage(); //NON-NLS
|
||||
|
||||
public static List<String> getSupportedExtensions() {
|
||||
return Collections.unmodifiableList(SUPP_EXTENSIONS);
|
||||
}
|
||||
|
||||
public static SortedSet<String> getSupportedMimeTypes() {
|
||||
return Collections.unmodifiableSortedSet(SUPP_MIME_TYPES);
|
||||
}
|
||||
//initialized lazily
|
||||
private static FileTypeDetector fileTypeDetector;
|
||||
|
||||
private static final List<String> SUPP_EXTENSIONS;
|
||||
private static final TreeSet<String> SUPP_MIME_TYPES;
|
||||
@ -90,11 +87,20 @@ public class ImageUtils {
|
||||
|
||||
SUPP_MIME_TYPES = new TreeSet<>(Arrays.asList(ImageIO.getReaderMIMETypes()));
|
||||
SUPP_MIME_TYPES.addAll(Arrays.asList("image/x-ms-bmp", "application/x-123"));
|
||||
SUPP_MIME_TYPES.removeIf("application/octet-stream"::equals);
|
||||
}
|
||||
|
||||
private ImageUtils() {
|
||||
}
|
||||
|
||||
public static List<String> getSupportedExtensions() {
|
||||
return Collections.unmodifiableList(SUPP_EXTENSIONS);
|
||||
}
|
||||
|
||||
public static SortedSet<String> getSupportedMimeTypes() {
|
||||
return Collections.unmodifiableSortedSet(SUPP_MIME_TYPES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default Icon, which is the icon for a file.
|
||||
*
|
||||
@ -122,12 +128,12 @@ public class ImageUtils {
|
||||
}
|
||||
|
||||
try {
|
||||
String mimeType = new FileTypeDetector().getFileType(file);
|
||||
String mimeType = getFileTypeDetector().getFileType(file);
|
||||
if (Objects.nonNull(mimeType)) {
|
||||
return SUPP_MIME_TYPES.contains(mimeType);
|
||||
return SUPP_MIME_TYPES.contains(mimeType)
|
||||
|| (mimeType.equalsIgnoreCase("audio/x-aiff") && "iff".equalsIgnoreCase(file.getNameExtension()));
|
||||
}
|
||||
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
||||
|
||||
LOGGER.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
|
||||
if (!SUPP_MIME_TYPES.isEmpty()) {
|
||||
AbstractFile.MimeMatchEnum mimeMatch = file.isMimeType(SUPP_MIME_TYPES);
|
||||
@ -141,15 +147,29 @@ public class ImageUtils {
|
||||
|
||||
// if we have an extension, check it
|
||||
final String extension = file.getNameExtension();
|
||||
if (StringUtils.isNotBlank(extension)) {
|
||||
if (SUPP_EXTENSIONS.contains(extension)) {
|
||||
return true;
|
||||
}
|
||||
if (StringUtils.isNotBlank(extension) && SUPP_EXTENSIONS.contains(extension)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if no extension or one that is not for an image, then read the content
|
||||
return isJpegFileHeader(file) || isPngFileHeader(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* lazily instantiates and returns a FileTypeDetector
|
||||
*
|
||||
* @return a FileTypeDetector
|
||||
*
|
||||
* @throws FileTypeDetectorInitException if a initializing the
|
||||
* FileTypeDetector failed.
|
||||
*/
|
||||
synchronized private static FileTypeDetector getFileTypeDetector() throws FileTypeDetector.FileTypeDetectorInitException {
|
||||
if (fileTypeDetector == null) {
|
||||
fileTypeDetector = new FileTypeDetector();
|
||||
}
|
||||
return fileTypeDetector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a thumbnail of a specified size. Generates the image if it is
|
||||
* not already cached.
|
||||
@ -281,21 +301,28 @@ public class ImageUtils {
|
||||
/**
|
||||
* Generate a thumbnail and save it to specified location.
|
||||
*
|
||||
* @param content File to generate icon for
|
||||
* @param size the size of thumbnail to generate in pixels
|
||||
* @param saveFile Location to save thumbnail to
|
||||
* @param content File to generate icon for
|
||||
* @param size the size of thumbnail to generate in pixels
|
||||
* @param cacheFile Location to save thumbnail to
|
||||
*
|
||||
* @return Generated icon or a default icon if a thumbnail could not be
|
||||
* made.
|
||||
*/
|
||||
private static Image generateAndSaveThumbnail(Content content, int size, File saveFile) {
|
||||
BufferedImage thumbNail = generateThumbnail(content, size);
|
||||
if (Objects.nonNull(thumbNail)) {
|
||||
private static Image generateAndSaveThumbnail(Content content, int size, File cacheFile) {
|
||||
BufferedImage thumbnail = generateThumbnail(content, size);
|
||||
if (Objects.nonNull(thumbnail)) {
|
||||
imageSaver.execute(() -> {
|
||||
|
||||
saveThumbnail(content, thumbNail, saveFile);
|
||||
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: " + content, ex1); //NON-NLS
|
||||
}
|
||||
});
|
||||
return thumbNail;
|
||||
return thumbnail;
|
||||
} else {
|
||||
return getDefaultIcon();
|
||||
}
|
||||
@ -313,8 +340,7 @@ public class ImageUtils {
|
||||
@Nullable
|
||||
private static BufferedImage generateThumbnail(Content content, int iconSize) {
|
||||
|
||||
try (InputStream inputStream = new ReadContentInputStream(content);) {
|
||||
|
||||
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(content));) {
|
||||
BufferedImage bi = ImageIO.read(inputStream);
|
||||
if (bi == null) {
|
||||
LOGGER.log(Level.WARNING, "No image reader for file: {0}", content.getName()); //NON-NLS
|
||||
@ -323,7 +349,7 @@ public class ImageUtils {
|
||||
try {
|
||||
return ScalrWrapper.resizeFast(bi, iconSize);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// if resizing does not work due to extremely small height/width ratio,
|
||||
// if resizing does not work due to extreme aspect ratio,
|
||||
// crop the image instead.
|
||||
return ScalrWrapper.cropImage(bi, Math.min(iconSize, bi.getWidth()), Math.min(iconSize, bi.getHeight()));
|
||||
}
|
||||
@ -335,24 +361,4 @@ public class ImageUtils {
|
||||
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 thumbnail the thumbnail to save for the given DrawableFile
|
||||
*/
|
||||
static private void saveThumbnail(Content content, final RenderedImage thumbnail, File cacheFile) {
|
||||
try {
|
||||
Files.createParentDirs(cacheFile);
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.delete();
|
||||
}
|
||||
//convert back to swing to save
|
||||
ImageIO.write(thumbnail, FORMAT, cacheFile);
|
||||
} catch (IllegalArgumentException | IOException ex) {
|
||||
LOGGER.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.Action;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
|
||||
@ -118,7 +118,7 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
|
||||
|
||||
// Images
|
||||
for (String s : FileTypeExtensions.getImageExtensions()) {
|
||||
if (ext.equals(s)) {
|
||||
if (ImageUtils.thumbnailSupported(file) || ext.equals(s)) {
|
||||
return "org/sleuthkit/autopsy/images/image-file.png"; //NON-NLS
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user