Skip to content

Commit ab1f33b

Browse files
authored
Merge pull request #2592 from ControlSystemStudio/CSSTUDIO-1869
Add watermark to image in Image Viewer app
2 parents b20ccda + 8f37882 commit ab1f33b

File tree

11 files changed

+190
-52
lines changed

11 files changed

+190
-52
lines changed
-18.5 KB
Binary file not shown.
8.18 KB
Loading

app/imageviewer/doc/index.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,25 @@ Overview
77
As the name suggests, the Image Viewer renders an image file at full resolution
88
in a separate tab in the main window. The tab title shows the image file name.
99

10-
Using the "Scale to fit" button user may rescale the non-SVG images to fit to the current size of the view. The resizing
10+
Using the "Scale to fit" button user may rescale the images to fit to the current size of the view. The resizing
1111
process will honor the aspect ratio of the image, i.e. the resulting view may show empty space
1212
above/below the image, or to left/right of the image. Pressing the button again will switch back to showing the
1313
image at full resolution. It should be noted that if the image is smaller than the current size of the view,
1414
pressing the "Scale to fit" button will zoom in on the image.
1515

16-
.. image:: images/scale_to_fit.png
16+
In some cases it may be necessary to indicate that the image is static in nature and cannot be interacted with, e.g.
17+
when showing a screen shot of an OPI. Ticking the Watermark check box will add an overlay showing the text "WATERMARK"
18+
(may be customized).
19+
20+
.. image:: images/toolbar.png
1721

1822
There is no menu entry for the application. It is launched automatically for instance
1923
when user double clicks on an image file in the File Browser, or when user clicks on
2024
an image in the log entry attachment viewer.
2125

2226
Supported file types are png, jpg (jpeg) and gif. The application also supports Scalable Vector Graphics
23-
files, but user should keep in mind that SVG tools may generate incompatible files.
27+
files, but user should keep in mind that SVG tools may generate incompatible files. When a SVG file is rendered
28+
by the application the "Scale to fit" button is disabled.
2429

2530
If user has configured an external application for a particular image file extension, this is
2631
NOT overridden by the Image Viewer application.

app/imageviewer/src/main/java/org/phoebus/applications/imageviewer/ImageViewerController.java

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,22 @@
1919
package org.phoebus.applications.imageviewer;
2020

2121
import javafx.beans.property.SimpleBooleanProperty;
22+
import javafx.beans.property.SimpleDoubleProperty;
23+
import javafx.beans.property.SimpleObjectProperty;
2224
import javafx.embed.swing.SwingFXUtils;
2325
import javafx.fxml.FXML;
2426
import javafx.scene.Node;
2527
import javafx.scene.control.Button;
28+
import javafx.scene.control.CheckBox;
29+
import javafx.scene.control.Label;
2630
import javafx.scene.control.ScrollPane;
2731
import javafx.scene.image.Image;
2832
import javafx.scene.image.ImageView;
33+
import javafx.scene.layout.BorderPane;
34+
import javafx.scene.layout.Pane;
35+
import javafx.scene.text.Font;
36+
import javafx.scene.text.FontPosture;
37+
import javafx.scene.text.FontWeight;
2938
import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog;
3039
import org.phoebus.ui.javafx.svg.SVGTranscoder;
3140

@@ -40,7 +49,7 @@
4049
* Controller for the image viewer application UI. It intentionally does not
4150
* use the {@link org.phoebus.ui.javafx.ImageCache} as the intention is to render
4251
* images at original - potentially very high - resolution.
43-
*
52+
* <p>
4453
* A button is available to scale the image to the current view size.
4554
*/
4655
public class ImageViewerController {
@@ -54,13 +63,31 @@ public class ImageViewerController {
5463
@FXML
5564
private ImageView imageView;
5665

66+
@FXML
67+
private Pane watermarkPane;
68+
5769
@FXML
5870
private Button scaleToFitButton;
5971

72+
@FXML
73+
private Label watermarkText;
74+
75+
@FXML
76+
private BorderPane imageParent;
77+
78+
@FXML
79+
private CheckBox showWatermarkCheckBox;
80+
6081
private Image image;
6182

62-
private SimpleBooleanProperty isSVG = new SimpleBooleanProperty(false);
63-
private SimpleBooleanProperty fitToWindow = new SimpleBooleanProperty(false);
83+
private final SimpleBooleanProperty isSVG = new SimpleBooleanProperty(false);
84+
private final SimpleBooleanProperty fitToWindow = new SimpleBooleanProperty(false);
85+
86+
private final SimpleBooleanProperty showWatermarkProperty = new SimpleBooleanProperty(true);
87+
88+
private final SimpleObjectProperty fontProperty = new SimpleObjectProperty();
89+
90+
private final SimpleDoubleProperty rotationProperty = new SimpleDoubleProperty();
6491

6592
@FXML
6693
public void initialize() {
@@ -84,6 +111,16 @@ public void initialize() {
84111
scale();
85112
}
86113
});
114+
115+
fontProperty.set(new Font(20));
116+
117+
watermarkPane.visibleProperty().bind(showWatermarkProperty);
118+
119+
watermarkText.textProperty().set(ImageViewerPreferences.watermark_text);
120+
watermarkText.fontProperty().bind(fontProperty);
121+
watermarkText.getStylesheets().addAll(getClass().getResource("/style.css").toExternalForm());
122+
watermarkText.getStyleClass().add("outline");
123+
watermarkText.rotateProperty().bind(rotationProperty);
87124
}
88125

89126
public Node getRoot() {
@@ -94,9 +131,11 @@ public Node getRoot() {
94131
* Sets the image in the view. Shows error dialog if the image file
95132
* cannot be loaded/rendered, e.g. non-image file or incompatible SVG.
96133
*
97-
* @param url
134+
* @param url URL to image resource
98135
*/
99-
public void setImage(URL url) {
136+
public void setImage(URL url, boolean showWatermark) {
137+
showWatermarkProperty.set(showWatermark);
138+
showWatermarkCheckBox.selectedProperty().set(showWatermark);
100139
try {
101140
if (url.toExternalForm().endsWith("svg")) {
102141
image = SVGTranscoder.loadSVG(url.openStream(), 0, 0);
@@ -109,6 +148,9 @@ public void setImage(URL url) {
109148
image = SwingFXUtils.toFXImage(bufferedImage, null);
110149
}
111150
imageView.setImage(image);
151+
imageParent.layout();
152+
scale();
153+
112154
} catch (Exception e) {
113155
ExceptionDetailsErrorDialog.openError(root,
114156
Messages.ErrorDialogTitle,
@@ -132,5 +174,33 @@ private void scale() {
132174
imageView.setFitWidth(image.getWidth());
133175
imageView.setFitHeight(image.getHeight());
134176
}
177+
fontProperty.set(getFont());
178+
setRotation();
179+
}
180+
181+
public void toggleWatermark() {
182+
showWatermarkProperty.set(!showWatermarkProperty.getValue());
183+
}
184+
185+
/**
186+
* Computes a font size based on current size of image view and length of the watermark text.
187+
* In short: larger image view -> larger font, longer text -> smaller font.
188+
*
189+
* @return A bold {@link Font} using calculated size.
190+
*/
191+
private Font getFont() {
192+
double height = imageView.getFitHeight();
193+
double width = imageView.getFitWidth();
194+
int textLength = ImageViewerPreferences.watermark_text.length();
195+
double scalingFactor = textLength / 2.0;
196+
return Font.font("Liberation Sans", FontWeight.BOLD, FontPosture.REGULAR, Math.min(width, height) / scalingFactor);
197+
}
198+
199+
/**
200+
* Determines rotation of the watermark text depending on portrait or landscape orientation of
201+
* the image. If portrait the text is rotated more to better fit it on top ov the image.
202+
*/
203+
private void setRotation() {
204+
rotationProperty.set(imageView.getFitHeight() < imageView.getFitWidth() ? 25.0 : 60.0);
135205
}
136206
}

app/imageviewer/src/main/java/org/phoebus/applications/imageviewer/ImageViewerInstance.java

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,59 +25,55 @@
2525
import org.phoebus.ui.docking.DockItem;
2626
import org.phoebus.ui.docking.DockItemWithInput;
2727
import org.phoebus.ui.docking.DockPane;
28-
import org.phoebus.ui.docking.DockStage;
2928

30-
import java.io.IOException;
31-
import java.io.InputStream;
32-
import java.net.MalformedURLException;
3329
import java.net.URI;
34-
import java.net.URL;
3530
import java.util.ResourceBundle;
3631
import java.util.logging.Level;
3732
import java.util.logging.Logger;
3833

3934
public class ImageViewerInstance implements AppInstance {
4035

41-
public static final String NAME = "Image Viewer";
42-
43-
public enum ViewMode{
36+
public enum ViewMode {
4437
TAB,
4538
DIALOG
46-
};
39+
}
4740

4841
private DockItem dockItem;
4942

50-
private AppDescriptor appDescriptor;
43+
private final AppDescriptor appDescriptor;
5144

52-
public ImageViewerInstance(AppDescriptor appDescriptor, URI uri, ViewMode viewMode){
45+
public ImageViewerInstance(AppDescriptor appDescriptor, URI uri, ViewMode viewMode) {
5346
this.appDescriptor = appDescriptor;
5447

55-
if(viewMode == ViewMode.TAB){
56-
final DockItemWithInput existing = DockStage.getDockItemWithInput(NAME, uri);
48+
if (viewMode == ViewMode.TAB) {
5749
FXMLLoader fxmlLoader = new FXMLLoader();
5850
ResourceBundle resourceBundle = NLS.getMessages(Messages.class);
5951
fxmlLoader.setResources(resourceBundle);
6052
fxmlLoader.setLocation(this.getClass().getResource("/ImageViewer.fxml"));
6153
try {
6254
fxmlLoader.load();
6355
ImageViewerController imageViewerController = fxmlLoader.getController();
64-
imageViewerController.setImage(uri.toURL());
6556
dockItem = new DockItemWithInput(this, imageViewerController.getRoot(), uri, null, null);
6657
DockPane.getActiveDockPane().addTab(dockItem);
58+
boolean showWatermark = false;
59+
String queryParams = uri.getQuery();
60+
if (queryParams != null && queryParams.contains("watermark=true")) {
61+
showWatermark = true;
62+
}
63+
imageViewerController.setImage(uri.toURL(), showWatermark);
6764
} catch (Exception e) {
6865
Logger.getLogger(ImageViewerInstance.class.getName())
69-
.log(Level.WARNING, "Unable to load fxml", e);
66+
.log(Level.WARNING, "Unable to load fxml", e);
7067
}
7168
}
7269
}
7370

7471
@Override
75-
public AppDescriptor getAppDescriptor()
76-
{
72+
public AppDescriptor getAppDescriptor() {
7773
return appDescriptor;
7874
}
7975

80-
public void raise(){
76+
public void raise() {
8177
dockItem.select();
8278
}
8379
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2018 Oak Ridge National Laboratory.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*******************************************************************************/
8+
package org.phoebus.applications.imageviewer;
9+
10+
import org.phoebus.framework.preferences.AnnotatedPreferences;
11+
import org.phoebus.framework.preferences.Preference;
12+
13+
/**
14+
* Preference settings for Image Viewer app
15+
*
16+
* @author Georg Weiss
17+
*/
18+
@SuppressWarnings("nls")
19+
public class ImageViewerPreferences {
20+
@Preference
21+
public static String watermark_text;
22+
23+
static {
24+
AnnotatedPreferences.initialize(ImageViewerPreferences.class, "/image_viewer_preferences.properties");
25+
}
26+
}
Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22

3-
<?import javafx.geometry.*?>
4-
<?import javafx.scene.control.*?>
5-
<?import javafx.scene.image.*?>
6-
<?import javafx.scene.layout.*?>
7-
83
<!--
94
~ Copyright (C) 2020 European Spallation Source ERIC.
105
~
@@ -23,21 +18,44 @@
2318
~ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2419
-->
2520

26-
<BorderPane fx:id="root" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.phoebus.applications.imageviewer.ImageViewerController">
27-
<top>
28-
<Button fx:id="scaleToFitButton" prefWidth="130.0" text="%ScaleToFit">
29-
<BorderPane.margin>
30-
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
31-
</BorderPane.margin></Button>
32-
</top>
33-
<center>
34-
<ScrollPane fx:id="scrollPane">
35-
<!-- this pane is needed to enable centering behavior -->
36-
<StackPane fx:id="stackPane">
37-
<children>
38-
<ImageView fx:id="imageView" />
39-
</children>
40-
</StackPane>
41-
</ScrollPane>
42-
</center>
21+
<?import javafx.geometry.Insets?>
22+
<?import javafx.scene.control.Button?>
23+
<?import javafx.scene.control.CheckBox?>
24+
<?import javafx.scene.control.Label?>
25+
<?import javafx.scene.control.ScrollPane?>
26+
<?import javafx.scene.control.ToolBar?>
27+
<?import javafx.scene.image.ImageView?>
28+
<?import javafx.scene.layout.BorderPane?>
29+
<?import javafx.scene.layout.StackPane?>
30+
<BorderPane fx:id="root" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
31+
fx:controller="org.phoebus.applications.imageviewer.ImageViewerController">
32+
<top>
33+
<ToolBar>
34+
<Button fx:id="scaleToFitButton" prefWidth="130.0" text="%ScaleToFit">
35+
</Button>
36+
<CheckBox fx:id="showWatermarkCheckBox" onAction="#toggleWatermark" prefWidth="130.0" selected="true"
37+
text="%watermarkButton">
38+
<padding>
39+
<Insets left="10.0"/>
40+
</padding>
41+
</CheckBox>
42+
</ToolBar>
43+
44+
</top>
45+
<center>
46+
<ScrollPane fx:id="scrollPane">
47+
<BorderPane fx:id="imageParent">
48+
<center>
49+
<StackPane fx:id="stackPane">
50+
<ImageView fx:id="imageView"/>
51+
<BorderPane fx:id="watermarkPane">
52+
<center>
53+
<Label fx:id="watermarkText" rotate="25.0" style="-fx-text-fill: #AAAAAA90;"/>
54+
</center>
55+
</BorderPane>
56+
</StackPane>
57+
</center>
58+
</BorderPane>
59+
</ScrollPane>
60+
</center>
4361
</BorderPane>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# --------------------------------------------
2+
# Package org.phoebus.applications.imageviewer
3+
# --------------------------------------------
4+
5+
# Watermark text
6+
watermark_text=W A T E R M A R K

app/imageviewer/src/main/resources/org/phoebus/applications/imageviewer/messages.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@
1919
ErrorDialogTitle=Error
2020
ErrorDialogText=Failed to load image resource {0}
2121
OneHundredPercent=Show 100%
22-
ScaleToFit=Scale to fit
22+
ScaleToFit=Scale to fit
23+
watermarkButton=Watermark
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.outline.label .text {
2+
-fx-stroke: #22222290;
3+
-fx-stroke-width: 1px;
4+
}

0 commit comments

Comments
 (0)