Zooming and rotation fully implemented.

This commit is contained in:
U-BASIS\dgrove 2019-01-14 02:04:30 -05:00
parent b76085490b
commit 79b6ddce4e
7 changed files with 365 additions and 128 deletions

View File

@ -76,7 +76,11 @@ SQLiteViewer.numEntriesField.text=num Entries
SQLiteViewer.jLabel1.text=Table SQLiteViewer.jLabel1.text=Table
PListViewer.exportButton.text=Export PListViewer.exportButton.text=Export
SQLiteViewer.exportCsvButton.text=Export to CSV SQLiteViewer.exportCsvButton.text=Export to CSV
MediaViewImagePanel.rotateLeftButton.text=Left MediaViewImagePanel.rotateLeftButton.text=
MediaViewImagePanel.zoomInButton.text=+ MediaViewImagePanel.zoomInButton.text=
MediaViewImagePanel.rotateRightButton.text=Right MediaViewImagePanel.rotateRightButton.text=
MediaViewImagePanel.zoomOutButton.text=- MediaViewImagePanel.zoomOutButton.text=
MediaViewImagePanel.zoomResetButton.text=Reset
MediaViewImagePanel.zoomTextField.text=
MediaViewImagePanel.rotationTextField.text=
MediaViewImagePanel.rotateLeftButton.toolTipText=

View File

@ -1,11 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.8" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="0" type="rgb"/> <Color blue="0" green="0" red="0" type="rgb"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="componentResized" listener="java.awt.event.ComponentListener" parameters="java.awt.event.ComponentEvent" handler="formComponentResized"/>
</Events>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -23,29 +26,58 @@
<Property name="axis" type="int" value="1"/> <Property name="axis" type="int" value="1"/>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Container class="javax.swing.JToolBar" name="jToolBar1"> <Container class="javax.swing.JToolBar" name="toolbar">
<Properties> <Properties>
<Property name="floatable" type="boolean" value="false"/> <Property name="floatable" type="boolean" value="false"/>
<Property name="rollover" type="boolean" value="true"/> <Property name="rollover" type="boolean" value="true"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[84, 23]"/> <Dimension value="[32767, 23]"/>
</Property> </Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/> <Property name="name" type="java.lang.String" value="" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[95, 23]"/>
</Property>
</Properties> </Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTextField" name="rotationTextField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.rotationTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 2147483647]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 20]"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="rotateLeftButton"> <Component class="javax.swing.JButton" name="rotateLeftButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/contentviewers/images/rotate-left.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.rotateLeftButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.rotateLeftButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.rotateLeftButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/> <Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/> <Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="verticalTextPosition" type="int" value="3"/> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rotateLeftButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rotateLeftButtonActionPerformed"/>
@ -53,12 +85,23 @@
</Component> </Component>
<Component class="javax.swing.JButton" name="rotateRightButton"> <Component class="javax.swing.JButton" name="rotateRightButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/contentviewers/images/rotate-right.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.rotateRightButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.rotateRightButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="focusable" type="boolean" value="false"/> <Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/> <Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="verticalTextPosition" type="int" value="3"/> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rotateRightButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rotateRightButtonActionPerformed"/>
@ -71,32 +114,92 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="zoomInButton"> <Component class="javax.swing.JTextField" name="zoomTextField">
<Properties> <Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="horizontalAlignment" type="int" value="4"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.zoomInButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.zoomTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 2147483647]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 20]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 20]"/>
</Property> </Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomInButtonActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="zoomOutButton"> <Component class="javax.swing.JButton" name="zoomOutButton">
<Properties> <Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/contentviewers/images/zoom-out.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.zoomOutButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.zoomOutButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="focusable" type="boolean" value="false"/> <Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/> <Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="verticalTextPosition" type="int" value="3"/> <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomOutButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomOutButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="zoomInButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/contentviewers/images/zoom-in.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.zoomInButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[24, 24]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomInButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JToolBar$Separator" name="jSeparator2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[6, 20]"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="zoomResetButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.zoomResetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomResetButtonActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,7 +18,6 @@
*/ */
package org.sleuthkit.autopsy.contentviewers; package org.sleuthkit.autopsy.contentviewers;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
@ -29,11 +28,8 @@ import java.util.SortedSet;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.collections.ObservableList;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.embed.swing.JFXPanel; import javafx.embed.swing.JFXPanel;
import javafx.geometry.Bounds;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D; import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor; import javafx.scene.Cursor;
@ -41,12 +37,14 @@ import javafx.scene.Scene;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar; import javafx.scene.control.ProgressBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.transform.Transform; import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.JPanel; import javax.swing.JPanel;
import org.controlsfx.control.MaskerPane; import org.controlsfx.control.MaskerPane;
@ -74,11 +72,19 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
private JFXPanel fxPanel; private JFXPanel fxPanel;
private ImageView fxImageView; private ImageView fxImageView;
private Pane pane; private ScrollPane scrollPane;
private final ProgressBar progressBar = new ProgressBar(); private final ProgressBar progressBar = new ProgressBar();
private final MaskerPane maskerPane = new MaskerPane(); private final MaskerPane maskerPane = new MaskerPane();
private double zoomRatio = 1.0; // 100% private double zoomRatio;
private double rotation; // Can be 0, 90, 180, and 270.
private static final double[] ZOOM_STEPS = {
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
private static final double MIN_ZOOM_RATIO = 0.0625; // 6.25%
private static final double MAX_ZOOM_RATIO = 10.0; // 1000%
static { static {
ImageIO.scanForPlugins(); ImageIO.scanForPlugins();
@ -110,18 +116,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
// build jfx ui (we could do this in FXML?) // build jfx ui (we could do this in FXML?)
fxImageView = new ImageView(); // will hold image fxImageView = new ImageView(); // will hold image
pane = new Pane(fxImageView); // centers and sizes imageview //DLG: scrollPane = new ScrollPane(fxImageView); // centers and sizes imageview
pane.getStyleClass().add("bg"); //NOI18N scrollPane.getStyleClass().add("bg"); //NOI18N
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
fxPanel = new JFXPanel(); // bridge jfx-swing fxPanel = new JFXPanel(); // bridge jfx-swing
Scene scene = new Scene(pane); //root of jfx tree Scene scene = new Scene(scrollPane); //root of jfx tree
scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N
fxPanel.setScene(scene); fxPanel.setScene(scene);
//bind size of image to that of scene, while keeping proportions //bind size of image to that of scene, while keeping proportions
fxImageView.fitWidthProperty().bind(scene.widthProperty());
fxImageView.fitHeightProperty().bind(scene.heightProperty());
fxImageView.setPreserveRatio(true);
fxImageView.setSmooth(true); fxImageView.setSmooth(true);
fxImageView.setCache(true); fxImageView.setCache(true);
@ -142,7 +147,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
public void reset() { public void reset() {
Platform.runLater(() -> { Platform.runLater(() -> {
fxImageView.setImage(null); fxImageView.setImage(null);
//DLG: borderpane.setsetCenter(null); scrollPane.setContent(null);
scrollPane.setContent(fxImageView);
}); });
} }
@ -159,7 +165,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton); final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton);
errorNode.setAlignment(Pos.CENTER); errorNode.setAlignment(Pos.CENTER);
//DLG: borderpane.setCenter(errorNode);
} }
/** /**
@ -193,16 +198,17 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
try { try {
Image fxImage = readImageTask.get(); Image fxImage = readImageTask.get();
if (nonNull(fxImage)) { if (nonNull(fxImage)) {
//we have non-null image show it // We have a non-null image, so let's show it.
fxImageView.setImage(fxImage); fxImageView.setImage(fxImage);
//DLG: borderpane.setCenter(fxImageView); resetView();
scrollPane.setContent(fxImageView);
} else { } else {
showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file); showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
} }
} catch (InterruptedException | ExecutionException ex) { } catch (InterruptedException | ExecutionException ex) {
showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file); showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
} }
pane.setCursor(Cursor.DEFAULT); scrollPane.setCursor(Cursor.DEFAULT);
}); });
readImageTask.setOnFailed(failed -> { readImageTask.setOnFailed(failed -> {
if (!Case.isCaseOpen()) { if (!Case.isCaseOpen()) {
@ -223,14 +229,14 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file); showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
} }
pane.setCursor(Cursor.DEFAULT); scrollPane.setCursor(Cursor.DEFAULT);
}); });
maskerPane.setProgressNode(progressBar); maskerPane.setProgressNode(progressBar);
progressBar.progressProperty().bind(readImageTask.progressProperty()); progressBar.progressProperty().bind(readImageTask.progressProperty());
maskerPane.textProperty().bind(readImageTask.messageProperty()); maskerPane.textProperty().bind(readImageTask.messageProperty());
//DLG: borderpane.setCenter(maskerPane); scrollPane.setContent(null);
pane.setCursor(Cursor.WAIT); scrollPane.setCursor(Cursor.WAIT);
new Thread(readImageTask).start(); new Thread(readImageTask).start();
}); });
} }
@ -276,153 +282,277 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
jToolBar1 = new javax.swing.JToolBar(); toolbar = new javax.swing.JToolBar();
rotationTextField = new javax.swing.JTextField();
rotateLeftButton = new javax.swing.JButton(); rotateLeftButton = new javax.swing.JButton();
rotateRightButton = new javax.swing.JButton(); rotateRightButton = new javax.swing.JButton();
jSeparator1 = new javax.swing.JToolBar.Separator(); jSeparator1 = new javax.swing.JToolBar.Separator();
zoomInButton = new javax.swing.JButton(); zoomTextField = new javax.swing.JTextField();
zoomOutButton = new javax.swing.JButton(); zoomOutButton = new javax.swing.JButton();
zoomInButton = new javax.swing.JButton();
jSeparator2 = new javax.swing.JToolBar.Separator();
zoomResetButton = new javax.swing.JButton();
setBackground(new java.awt.Color(0, 0, 0)); setBackground(new java.awt.Color(0, 0, 0));
addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentResized(java.awt.event.ComponentEvent evt) {
formComponentResized(evt);
}
});
setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS)); setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS));
jToolBar1.setFloatable(false); toolbar.setFloatable(false);
jToolBar1.setRollover(true); toolbar.setRollover(true);
jToolBar1.setMaximumSize(new java.awt.Dimension(84, 23)); toolbar.setMaximumSize(new java.awt.Dimension(32767, 23));
jToolBar1.setName(""); // NOI18N toolbar.setName(""); // NOI18N
jToolBar1.setPreferredSize(new java.awt.Dimension(95, 23));
rotationTextField.setEditable(false);
rotationTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
rotationTextField.setText(org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.rotationTextField.text")); // NOI18N
rotationTextField.setMaximumSize(new java.awt.Dimension(50, 2147483647));
rotationTextField.setMinimumSize(new java.awt.Dimension(50, 20));
rotationTextField.setPreferredSize(new java.awt.Dimension(50, 20));
toolbar.add(rotationTextField);
rotateLeftButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/rotate-left.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(rotateLeftButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.rotateLeftButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(rotateLeftButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.rotateLeftButton.text")); // NOI18N
rotateLeftButton.setToolTipText(org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.rotateLeftButton.toolTipText")); // NOI18N
rotateLeftButton.setFocusable(false); rotateLeftButton.setFocusable(false);
rotateLeftButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); rotateLeftButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
rotateLeftButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); rotateLeftButton.setMaximumSize(new java.awt.Dimension(24, 24));
rotateLeftButton.setMinimumSize(new java.awt.Dimension(24, 24));
rotateLeftButton.setPreferredSize(new java.awt.Dimension(24, 24));
rotateLeftButton.addActionListener(new java.awt.event.ActionListener() { rotateLeftButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
rotateLeftButtonActionPerformed(evt); rotateLeftButtonActionPerformed(evt);
} }
}); });
jToolBar1.add(rotateLeftButton); toolbar.add(rotateLeftButton);
rotateRightButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/rotate-right.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(rotateRightButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.rotateRightButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(rotateRightButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.rotateRightButton.text")); // NOI18N
rotateRightButton.setFocusable(false); rotateRightButton.setFocusable(false);
rotateRightButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); rotateRightButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
rotateRightButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); rotateRightButton.setMaximumSize(new java.awt.Dimension(24, 24));
rotateRightButton.setMinimumSize(new java.awt.Dimension(24, 24));
rotateRightButton.setPreferredSize(new java.awt.Dimension(24, 24));
rotateRightButton.addActionListener(new java.awt.event.ActionListener() { rotateRightButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
rotateRightButtonActionPerformed(evt); rotateRightButtonActionPerformed(evt);
} }
}); });
jToolBar1.add(rotateRightButton); toolbar.add(rotateRightButton);
jSeparator1.setMaximumSize(new java.awt.Dimension(6, 20)); jSeparator1.setMaximumSize(new java.awt.Dimension(6, 20));
jToolBar1.add(jSeparator1); toolbar.add(jSeparator1);
org.openide.awt.Mnemonics.setLocalizedText(zoomInButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.zoomInButton.text")); // NOI18N zoomTextField.setEditable(false);
zoomInButton.setFocusable(false); zoomTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
zoomInButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); zoomTextField.setText(org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.zoomTextField.text")); // NOI18N
zoomInButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); zoomTextField.setMaximumSize(new java.awt.Dimension(50, 2147483647));
zoomInButton.addActionListener(new java.awt.event.ActionListener() { zoomTextField.setMinimumSize(new java.awt.Dimension(50, 20));
public void actionPerformed(java.awt.event.ActionEvent evt) { zoomTextField.setPreferredSize(new java.awt.Dimension(50, 20));
zoomInButtonActionPerformed(evt); toolbar.add(zoomTextField);
}
});
jToolBar1.add(zoomInButton);
zoomOutButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/zoom-out.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(zoomOutButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.zoomOutButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(zoomOutButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.zoomOutButton.text")); // NOI18N
zoomOutButton.setFocusable(false); zoomOutButton.setFocusable(false);
zoomOutButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); zoomOutButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
zoomOutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); zoomOutButton.setMaximumSize(new java.awt.Dimension(24, 24));
zoomOutButton.setMinimumSize(new java.awt.Dimension(24, 24));
zoomOutButton.setPreferredSize(new java.awt.Dimension(24, 24));
zoomOutButton.addActionListener(new java.awt.event.ActionListener() { zoomOutButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
zoomOutButtonActionPerformed(evt); zoomOutButtonActionPerformed(evt);
} }
}); });
jToolBar1.add(zoomOutButton); toolbar.add(zoomOutButton);
add(jToolBar1); zoomInButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/contentviewers/images/zoom-in.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(zoomInButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.zoomInButton.text")); // NOI18N
zoomInButton.setFocusable(false);
zoomInButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
zoomInButton.setMaximumSize(new java.awt.Dimension(24, 24));
zoomInButton.setMinimumSize(new java.awt.Dimension(24, 24));
zoomInButton.setPreferredSize(new java.awt.Dimension(24, 24));
zoomInButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
zoomInButtonActionPerformed(evt);
}
});
toolbar.add(zoomInButton);
jSeparator2.setMaximumSize(new java.awt.Dimension(6, 20));
toolbar.add(jSeparator2);
org.openide.awt.Mnemonics.setLocalizedText(zoomResetButton, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.zoomResetButton.text")); // NOI18N
zoomResetButton.setFocusable(false);
zoomResetButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
zoomResetButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
zoomResetButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
zoomResetButtonActionPerformed(evt);
}
});
toolbar.add(zoomResetButton);
add(toolbar);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed private void rotateLeftButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateLeftButtonActionPerformed
fxImageView.setRotate(fxImageView.getRotate() - 90); rotation = (rotation + 270) % 360;
updateView();
test();
}//GEN-LAST:event_rotateLeftButtonActionPerformed }//GEN-LAST:event_rotateLeftButtonActionPerformed
private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed private void rotateRightButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rotateRightButtonActionPerformed
fxImageView.setRotate(fxImageView.getRotate() + 90); rotation = (rotation + 90) % 360;
updateView();
test();
}//GEN-LAST:event_rotateRightButtonActionPerformed }//GEN-LAST:event_rotateRightButtonActionPerformed
private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
if (zoomRatio >= 1.0) { for (int i=0; i < ZOOM_STEPS.length; i++) {
zoomRatio += 1.0; if (zoomRatio < ZOOM_STEPS[i]) {
if (zoomRatio == 8.0) { zoomRatio = ZOOM_STEPS[i];
zoomInButton.setEnabled(false); break;
} }
} else {
zoomRatio *= 2;
} }
zoomOutButton.setEnabled(true); updateView();
//double x = 0;
//double y = 0;
//double width = 0;
//double height = 0;
//fxImageView.setViewport(new Rectangle2D(x, y, width, height));
test();
fxImageView.setScaleX(zoomRatio);
fxImageView.setScaleY(zoomRatio);
}//GEN-LAST:event_zoomInButtonActionPerformed }//GEN-LAST:event_zoomInButtonActionPerformed
private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
if (zoomRatio <= 1.0) { for (int i=ZOOM_STEPS.length-1; i >= 0; i--) {
zoomRatio /= 2; if (zoomRatio > ZOOM_STEPS[i]) {
if (zoomRatio == 0.125) { zoomRatio = ZOOM_STEPS[i];
zoomOutButton.setEnabled(false); break;
} }
} else {
zoomRatio -= 1.0;
} }
zoomInButton.setEnabled(true); updateView();
test();
//double x = 0;
//double y = 0;
//double width = 0;
//double height = 0;
//fxImageView.setViewport(new Rectangle2D(x, y, width, height));
fxImageView.setScaleX(zoomRatio);
fxImageView.setScaleY(zoomRatio);
}//GEN-LAST:event_zoomOutButtonActionPerformed }//GEN-LAST:event_zoomOutButtonActionPerformed
private void zoomResetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomResetButtonActionPerformed
resetView();
}//GEN-LAST:event_zoomResetButtonActionPerformed
private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
updateView();
}//GEN-LAST:event_formComponentResized
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JToolBar.Separator jSeparator1;
private javax.swing.JToolBar jToolBar1; private javax.swing.JToolBar.Separator jSeparator2;
private javax.swing.JButton rotateLeftButton; private javax.swing.JButton rotateLeftButton;
private javax.swing.JButton rotateRightButton; private javax.swing.JButton rotateRightButton;
private javax.swing.JTextField rotationTextField;
private javax.swing.JToolBar toolbar;
private javax.swing.JButton zoomInButton; private javax.swing.JButton zoomInButton;
private javax.swing.JButton zoomOutButton; private javax.swing.JButton zoomOutButton;
private javax.swing.JButton zoomResetButton;
private javax.swing.JTextField zoomTextField;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
//DLG: Remove this! /**
private void test() { * Reset the zoom and rotation values to their defaults. The zoom level gets
double layoutX = fxImageView.getLayoutX(); * defaulted to the current size of the panel. The rotation will be set to
double layoutY = fxImageView.getLayoutY(); * zero.
*/
private void resetView() {
Image image = fxImageView.getImage();
if (image == null) {
return;
}
Bounds layoutBounds = fxImageView.getLayoutBounds(); double imageWidth = image.getWidth();
double imageHeight = image.getHeight();
double scrollPaneWidth = fxPanel.getWidth();
double scrollPaneHeight = fxPanel.getHeight();
double zoomRatioWidth = (scrollPaneWidth) / (imageWidth + 1);
double zoomRatioHeight = (scrollPaneHeight) / (imageHeight + 1);
DoubleProperty scaleXProperty = fxImageView.scaleXProperty(); // Use the smallest ratio size to fit the entire image in the view area.
DoubleProperty scaleYProperty = fxImageView.scaleXProperty(); zoomRatio = zoomRatioWidth < zoomRatioHeight ? zoomRatioWidth : zoomRatioHeight;
ObservableList<Transform> transforms = fxImageView.getTransforms(); rotation = 0;
double translateX = fxImageView.getTranslateX(); scrollPane.setHvalue(0);
double translateY = fxImageView.getTranslateY(); scrollPane.setVvalue(0);
System.out.println(); updateView();
}
/**
* Update the image to use the current zoom and rotation values.
*/
private void updateView() {
Image image = fxImageView.getImage();
if (image == null) {
return;
}
double imageWidth = image.getWidth();
double imageHeight = image.getHeight();
double adjustedImageWidth = imageWidth * zoomRatio;
double adjustedImageHeight = imageHeight * zoomRatio;
double viewportWidth;
double viewportHeight;
double centerOffsetX = (fxPanel.getWidth() / 2) - (imageWidth / 2);
double centerOffsetY = (fxPanel.getHeight() / 2) - (imageHeight / 2);
double leftOffsetX;
double topOffsetY;
// Set viewport size and translation offsets.
if ((rotation % 180) == 0) {
// Rotation is 0 or 180.
viewportWidth = adjustedImageWidth;
viewportHeight = adjustedImageHeight;
leftOffsetX = (adjustedImageWidth - imageWidth) / 2;
topOffsetY = (adjustedImageHeight - imageHeight) / 2;
} else {
// Rotation is 90 or 270.
viewportWidth = adjustedImageHeight;
viewportHeight = adjustedImageWidth;
leftOffsetX = (adjustedImageHeight - imageWidth) / 2;
topOffsetY = (adjustedImageWidth - imageHeight) / 2;
}
// Work around bug that truncates image if dimensions are too small.
if (viewportWidth < imageWidth) {
viewportWidth = imageWidth;
}
if (viewportHeight < imageHeight) {
viewportHeight = imageHeight;
}
// Update the viewport size.
fxImageView.setViewport(new Rectangle2D(
0, 0, viewportWidth, viewportHeight));
// Step 1: Zoom
Scale scale = new Scale();
scale.setX(zoomRatio);
scale.setY(zoomRatio);
scale.setPivotX(imageWidth / 2);
scale.setPivotY(imageHeight / 2);
// Step 2: Rotate
Rotate rotate = new Rotate();
rotate.setPivotX(imageWidth / 2);
rotate.setPivotY(imageHeight / 2);
rotate.setAngle(rotation);
// Step 3: Position
Translate translate = new Translate();
translate.setX(viewportWidth > fxPanel.getWidth() ? leftOffsetX : centerOffsetX);
translate.setY(viewportHeight > fxPanel.getHeight() ? topOffsetY : centerOffsetY);
fxImageView.getTransforms().clear();
fxImageView.getTransforms().add(translate);
fxImageView.getTransforms().add(rotate);
fxImageView.getTransforms().add(scale);
// Update all image controls to reflect the current values.
zoomOutButton.setEnabled(zoomRatio > MIN_ZOOM_RATIO);
zoomInButton.setEnabled(zoomRatio < MAX_ZOOM_RATIO);
rotationTextField.setText((int) rotation + "°");
zoomTextField.setText((Math.round(zoomRatio * 100.0)) + "%");
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B