diff --git a/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java b/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java index 645f4615c7..7798812059 100644 --- a/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java +++ b/app/display/model/src/main/java/org/csstudio/display/builder/model/Messages.java @@ -267,6 +267,7 @@ public class Messages WidgetProperties_ReadbackPVName, WidgetProperties_ReadbackPVValue, WidgetProperties_ResizeBehavior, + WidgetProperties_SVGRenderingResolutionFactor, WidgetProperties_RingColor, WidgetProperties_RingWidth, WidgetProperties_Rotation, diff --git a/app/display/model/src/main/java/org/csstudio/display/builder/model/Preferences.java b/app/display/model/src/main/java/org/csstudio/display/builder/model/Preferences.java index 0b3317dbe1..827c27dbe7 100644 --- a/app/display/model/src/main/java/org/csstudio/display/builder/model/Preferences.java +++ b/app/display/model/src/main/java/org/csstudio/display/builder/model/Preferences.java @@ -37,6 +37,9 @@ public class Preferences @Preference(name="macros") private static String macro_spec; /** Preference setting */ @Preference public static boolean enable_saved_on_comments; + /** Preference setting */ + @Preference public static boolean enable_svg_rendering_resolution_factor; + private static Macros macros; static diff --git a/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/PictureWidget.java b/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/PictureWidget.java index a593563629..0d1b287d9c 100644 --- a/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/PictureWidget.java +++ b/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/PictureWidget.java @@ -15,6 +15,7 @@ import java.util.List; import org.csstudio.display.builder.model.Messages; +import org.csstudio.display.builder.model.Preferences; import org.csstudio.display.builder.model.Version; import org.csstudio.display.builder.model.Widget; import org.csstudio.display.builder.model.WidgetCategory; @@ -103,6 +104,10 @@ public boolean configureFromXML(final ModelReader model_reader, final Widget wid public static final WidgetPropertyDescriptor propStretch = newBooleanPropertyDescriptor(WidgetPropertyCategory.DISPLAY, "stretch_image", Messages.WidgetProperties_StretchToFit); + /** 'svg_rendering_resolution_factor': */ + public static final WidgetPropertyDescriptor propSVGRenderingResolutionFactor = + newDoublePropertyDescriptor(WidgetPropertyCategory.DISPLAY, "svg_rendering_resolution_factor", Messages.WidgetProperties_SVGRenderingResolutionFactor); + /** * An opacity property. Controlling it from a rule or script works as a * way to do a simple animation. @@ -114,6 +119,7 @@ public boolean configureFromXML(final ModelReader model_reader, final Widget wid private volatile WidgetProperty stretch_image; private volatile WidgetProperty rotation; private volatile WidgetProperty opacity; + private volatile WidgetProperty svgRenderingResolutionFactor; /** Constructor */ public PictureWidget() @@ -129,7 +135,10 @@ protected void defineProperties(final List> properties) properties.add(stretch_image = propStretch.createProperty(this, false)); properties.add(rotation = propRotation.createProperty(this, 0.0)); properties.add(opacity = propOpacity.createProperty(this, 1.0)); - + svgRenderingResolutionFactor = propSVGRenderingResolutionFactor.createProperty(this, 1.0); + if (Preferences.enable_svg_rendering_resolution_factor) { + properties.add(svgRenderingResolutionFactor); + } } /** @return 'rotation' property */ @@ -156,6 +165,11 @@ public WidgetProperty propOpacity() return opacity; } + /** @return 'svgRenderingResolutionFactor' property */ + public WidgetProperty propSVGRenderingResolutionFactor() { + return svgRenderingResolutionFactor; + } + @Override public WidgetConfigurator getConfigurator(final Version persisted_version) throws Exception diff --git a/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/SymbolWidget.java b/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/SymbolWidget.java index 19db03c438..c55466f88c 100644 --- a/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/SymbolWidget.java +++ b/app/display/model/src/main/java/org/csstudio/display/builder/model/widgets/SymbolWidget.java @@ -25,6 +25,7 @@ import org.csstudio.display.builder.model.ArrayWidgetProperty; import org.csstudio.display.builder.model.Messages; +import org.csstudio.display.builder.model.Preferences; import org.csstudio.display.builder.model.Version; import org.csstudio.display.builder.model.Widget; import org.csstudio.display.builder.model.WidgetCategory; @@ -87,6 +88,9 @@ public Widget createWidget ( ) { /** Property */ public static final WidgetPropertyDescriptor propFallbackSymbol = newFilenamePropertyDescriptor (WidgetPropertyCategory.BEHAVIOR, "fallback_symbol", Messages.WidgetProperties_FallbackSymbol); public static final WidgetPropertyDescriptor propRunActionsOnMouseClick = newBooleanPropertyDescriptor (WidgetPropertyCategory.BEHAVIOR, "run_actions_on_mouse_click", Messages.WidgetProperties_RunActionsOnMouseClick); + /** 'svg_rendering_resolution_factor': */ + public static final WidgetPropertyDescriptor propSVGRenderingResolutionFactor = newDoublePropertyDescriptor(WidgetPropertyCategory.DISPLAY, "svg_rendering_resolution_factor", Messages.WidgetProperties_SVGRenderingResolutionFactor); + /** 'items' property: list of items (string properties) for combo box */ public static final ArrayWidgetProperty.Descriptor > propSymbols = new ArrayWidgetProperty.Descriptor< >( @@ -121,6 +125,7 @@ public Widget createWidget ( ) { private volatile WidgetProperty fallbackSymbol; private volatile WidgetProperty disconnectOverlayColor; private volatile WidgetProperty run_actions_on_mouse_click; + private volatile WidgetProperty svgRenderingResolutionFactor; /** Returns 'symbol' property: element for list of 'symbols' property */ private static WidgetPropertyDescriptor propSymbol( int index ) { @@ -225,6 +230,11 @@ public WidgetProperty propRunActionsOnMouseClick() { return run_actions_on_mouse_click; } + /** @return 'svgRenderingResolutionFactor' property */ + public WidgetProperty propSVGRenderingResolutionFactor() { + return svgRenderingResolutionFactor; + } + @Override protected void defineProperties ( final List> properties ) { @@ -243,6 +253,12 @@ protected void defineProperties ( final List> properties ) { properties.add(enabled = propEnabled.createProperty(this, true)); properties.add(preserve_ratio = propPreserveRatio.createProperty(this, true)); properties.add(fallbackSymbol = propFallbackSymbol.createProperty(this, DEFAULT_SYMBOL)); + + svgRenderingResolutionFactor = propSVGRenderingResolutionFactor.createProperty(this, 1.0); + if (Preferences.enable_svg_rendering_resolution_factor) { + properties.add(svgRenderingResolutionFactor); + } + WidgetColor alarmInvalidColor = WidgetColorService.getColor(NamedWidgetColors.ALARM_INVALID); WidgetColor defaultDisconnectedOverlayColor = diff --git a/app/display/model/src/main/resources/display_model_preferences.properties b/app/display/model/src/main/resources/display_model_preferences.properties index e5f3a3f4e1..196478983f 100644 --- a/app/display/model/src/main/resources/display_model_preferences.properties +++ b/app/display/model/src/main/resources/display_model_preferences.properties @@ -88,3 +88,10 @@ skip_defaults=true # Add a comment containing the date, time, and username when saving an OPI in the Display Editor. enable_saved_on_comments=true + +# Enable the "SVG Rendering Resolution Factor" widget property to the Symbol and Picture widgets. +# This functionality can enable a sharper image when zooming in, at the expense of a quadratic +# increase in memory consumption. E.g., by setting the SVG rendering resolution factor to 2.0, an +# SVG can be rendered at twice the width and twice the height, but the memory required to +# represent the image grows by a factor of four. +org.csstudio.display.builder.model/enable_svg_rendering_resolution_factor=false diff --git a/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties b/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties index da6edfdf5d..3eb1a7853b 100644 --- a/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties +++ b/app/display/model/src/main/resources/org/csstudio/display/builder/model/messages.properties @@ -285,6 +285,7 @@ WidgetProperties_Skin=Skin WidgetProperties_Square=Square WidgetProperties_StartFromZero=Start From Zero WidgetProperties_StretchToFit=Stretch to Fit +WidgetProperties_SVGRenderingResolutionFactor=SVG Rendering Resolution Factor WidgetProperties_Symbol=Symbol WidgetProperties_Symbols=Symbols WidgetProperties_SymbolValue=Text Symbol value diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/PictureRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/PictureRepresentation.java index e0ae845d98..c3d5767f28 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/PictureRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/PictureRepresentation.java @@ -45,6 +45,7 @@ public class PictureRepresentation extends JFXBaseRepresentation contentChangedListener = this::contentChanged; private volatile Image img_loaded; @@ -96,6 +97,7 @@ protected void registerListeners() model_widget.propStretch().addUntypedPropertyListener(styleChangedListener); model_widget.propRotation().addUntypedPropertyListener(styleChangedListener); model_widget.propOpacity().addUntypedPropertyListener(styleChangedListener); + model_widget.propSVGRenderingResolutionFactor().addUntypedPropertyListener(svgRenderingResolutionFactorChangedListener); // styleChanged() will be called by contentChanged() model_widget.propFile().addPropertyListener(contentChangedListener); @@ -117,6 +119,7 @@ protected void unregisterListeners() model_widget.propRotation().removePropertyListener(styleChangedListener); model_widget.propOpacity().removePropertyListener(styleChangedListener); model_widget.propFile().removePropertyListener(contentChangedListener); + model_widget.propSVGRenderingResolutionFactor().removePropertyListener(svgRenderingResolutionFactorChangedListener); super.unregisterListeners(); } @@ -126,6 +129,12 @@ private void styleChanged(final WidgetProperty property, final Object old_val toolkit.scheduleUpdate(this); } + private void svgRenderingResolutionFactorChanged(final WidgetProperty property, final Object oldValue, Object newValue) { + // Update the rendered SVG: + contentChanged(model_widget.propFile(), model_widget.propFile().getValue(), model_widget.propFile().getValue()); + } + + private void contentChanged(final WidgetProperty property, final String old_value, final String new_value) { // Imagine if updateChanges executes here. Mark is cleared and image updated before new image loaded. @@ -262,10 +271,23 @@ else if (h_prime < pic_h) * @param height * @return An {@link Image} or null. */ - private void loadSVG(String fileName, double width, double height){ - + private void loadSVG(String fileName, double width, double height) { String imageFileName = resolveImageFile(fileName); - img_loaded = SVGHelper.loadSVG(imageFileName, width, height); + + double svg_rendering_resolution_factor = model_widget.propSVGRenderingResolutionFactor().getValue(); + + double renderingWidth; + double renderingHeight; + if (!Double.isNaN(svg_rendering_resolution_factor) && svg_rendering_resolution_factor > 0) { + renderingWidth = svg_rendering_resolution_factor * width; + renderingHeight = svg_rendering_resolution_factor * height; + } else { + logger.log(Level.WARNING, "The SVG Rendering Factor is not set to a value greater than 0.0! Setting it to 1.0."); + renderingWidth = width; + renderingHeight = height; + } + + img_loaded = SVGHelper.loadSVG(imageFileName, renderingWidth, renderingHeight); if(img_loaded == null){ loadDefaultImage(); } diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SymbolRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SymbolRepresentation.java index 2dc7965da8..65643470ed 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SymbolRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SymbolRepresentation.java @@ -100,6 +100,7 @@ public class SymbolRepresentation extends RegionBaseRepresentation valueListener = this::valueChanged; private final WidgetPropertyListener>> symbolsListener = this::symbolsChanged; private final WidgetPropertyListener indexListener = this::initialIndexChanged; @@ -610,6 +611,7 @@ protected void registerListeners() model_widget.propEnabled().addUntypedPropertyListener(styleListener); model_widget.propShowIndex().addUntypedPropertyListener(styleListener); model_widget.propTransparent().addUntypedPropertyListener(styleListener); + model_widget.propSVGRenderingResolutionFactor().addUntypedPropertyListener(svgRenderingResolutionFactorChangedListener); if (!toolkit.isEditMode()) model_widget.runtimePropValue().addPropertyListener(valueListener); @@ -639,6 +641,7 @@ protected void unregisterListeners() model_widget.propEnabled().removePropertyListener(styleListener); model_widget.propShowIndex().removePropertyListener(styleListener); model_widget.propTransparent().removePropertyListener(styleListener); + model_widget.propSVGRenderingResolutionFactor().removePropertyListener(svgRenderingResolutionFactorChangedListener); if (!toolkit.isEditMode()) model_widget.runtimePropValue().removePropertyListener(valueListener); @@ -788,6 +791,11 @@ void setSymbolSize ( double width, double height, boolean preserveRatio ) { } } + private void svgRenderingResolutionFactorChanged(final WidgetProperty property, final Object oldValue, Object newValue) { + // Update the rendered SVG: + symbolChanged(null, null, null); + } + private void styleChanged ( final WidgetProperty property, final Object oldValue, final Object newValue ) { dirtyStyle.mark(); toolkit.scheduleUpdate(this); @@ -1027,7 +1035,20 @@ void resize(double width, double height, boolean preserveRatio){ * @return An {@link Image} or null. */ Image loadSVG(final String imageFileName, double width, double height){ - return SVGHelper.loadSVG(imageFileName, width, height); + double svg_rendering_resolution_factor = model_widget.propSVGRenderingResolutionFactor().getValue(); + + double renderingWidth; + double renderingHeight; + if (!Double.isNaN(svg_rendering_resolution_factor) && svg_rendering_resolution_factor > 0) { + renderingWidth = svg_rendering_resolution_factor * width; + renderingHeight = svg_rendering_resolution_factor * height; + } else { + logger.log(Level.WARNING, "The SVG Rendering Factor is not set to a value greater than 0.0! Setting it to 1.0."); + renderingWidth = width; + renderingHeight = height; + } + + return SVGHelper.loadSVG(imageFileName, renderingWidth, renderingHeight); } } }