Index: trunk/src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java	(revision 17739)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java	(revision 17740)
@@ -70,5 +70,6 @@
     private static final double DEFAULT_COLORFUL_FACTOR = 1;
     private static final double MAX_COLORFUL_FACTOR = 2;
-    private final LayerListModel model;
+    private final Supplier<Collection<Layer>> layerSupplier;
+    private final Supplier<Collection<ImageryFilterSettings>> filterSettingsSupplier;
     private final JPopupMenu popup;
     private SideButton sideButton;
@@ -85,5 +86,16 @@
      */
     public LayerVisibilityAction(LayerListModel model) {
-        this.model = model;
+        this(model::getSelectedLayers, () ->
+                Utils.transform(Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class), ImageryLayer::getFilterSettings));
+    }
+
+    /**
+     * Creates a new {@link LayerVisibilityAction}
+     * @param layerSupplier supplies the layers which should be affected
+     * @param filterSettingsSupplier supplies the filter settings which should be affecgted
+     */
+    public LayerVisibilityAction(Supplier<Collection<Layer>> layerSupplier, Supplier<Collection<ImageryFilterSettings>> filterSettingsSupplier) {
+        this.layerSupplier = layerSupplier;
+        this.filterSettingsSupplier = filterSettingsSupplier;
         popup = new JPopupMenu();
         // prevent popup close on mouse wheel move
@@ -103,5 +115,5 @@
         addContentEntry(new GammaFilterSlider());
         addContentEntry(new SharpnessSlider());
-        addContentEntry(new ColorSelector(model::getSelectedLayers));
+        addContentEntry(new ColorSelector());
     }
 
@@ -112,5 +124,5 @@
 
     void setVisibleFlag(boolean visible) {
-        for (Layer l : model.getSelectedLayers()) {
+        for (Layer l : layerSupplier.get()) {
             l.setVisible(visible);
         }
@@ -136,15 +148,6 @@
 
     void updateValues() {
-        List<Layer> layers = model.getSelectedLayers();
-
-        boolean allVisible = true;
-        boolean allHidden = true;
-        for (Layer l : layers) {
-            allVisible &= l.isVisible();
-            allHidden &= !l.isVisible();
-        }
-
         for (LayerVisibilityMenuEntry slider : sliders) {
-            slider.updateLayers(layers, allVisible, allHidden);
+            slider.updateLayers();
         }
     }
@@ -162,5 +165,5 @@
     @Override
     public void updateEnabledState() {
-        setEnabled(!model.getSelectedLayers().isEmpty());
+        setEnabled(!layerSupplier.get().isEmpty() || !filterSettingsSupplier.get().isEmpty());
     }
 
@@ -181,9 +184,6 @@
         /**
          * Update the displayed value depending on the current layers
-         * @param layers The layers
-         * @param allVisible <code>true</code> if all layers are visible
-         * @param allHidden <code>true</code> if all layers are hidden
-         */
-        void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden);
+         */
+        void updateLayers();
 
         /**
@@ -207,5 +207,9 @@
 
         @Override
-        public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) {
+        public void updateLayers() {
+            Collection<Layer> layers = layerSupplier.get();
+            boolean allVisible = layers.stream().allMatch(Layer::isVisible);
+            boolean allHidden = layers.stream().noneMatch(Layer::isVisible);
+
             setEnabled(!layers.isEmpty());
             // TODO: Indicate tristate.
@@ -222,11 +226,8 @@
      * This is a slider for a filter value.
      * @author Michael Zangl
-     *
-     * @param <T> The layer type.
-     */
-    private abstract class AbstractFilterSlider<T extends Layer> extends JPanel implements LayerVisibilityMenuEntry {
+     */
+    private abstract class AbstractFilterSlider extends JPanel implements LayerVisibilityMenuEntry {
         private final double minValue;
         private final double maxValue;
-        private final Class<T> layerClassFilter;
 
         protected final JSlider slider = new JSlider(JSlider.HORIZONTAL);
@@ -237,11 +238,9 @@
          * @param maxValue The maximum value to map to the right side.
          * @param defaultValue The default value for resetting.
-         * @param layerClassFilter The type of layer influenced by this filter.
-         */
-        AbstractFilterSlider(double minValue, double maxValue, double defaultValue, Class<T> layerClassFilter) {
+         */
+        AbstractFilterSlider(double minValue, double maxValue, double defaultValue) {
             super(new GridBagLayout());
             this.minValue = minValue;
             this.maxValue = maxValue;
-            this.layerClassFilter = layerClassFilter;
 
             add(new JLabel(getIcon()), GBC.std().span(1, 2).insets(0, 0, 5, 0));
@@ -284,10 +283,5 @@
          * @see #getRealValue()
          */
-        protected void onStateChanged() {
-            Collection<T> layers = filterLayers(model.getSelectedLayers());
-            for (T layer : layers) {
-                applyValueToLayer(layer);
-            }
-        }
+        protected abstract void onStateChanged();
 
         protected void mouseWheelMoved(MouseWheelEvent e) {
@@ -307,6 +301,4 @@
         }
 
-        abstract void applyValueToLayer(T layer);
-
         protected double getRealValue() {
             return convertToRealValue(slider.getValue());
@@ -330,22 +322,4 @@
 
         public abstract String getLabel();
-
-        @Override
-        public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) {
-            Collection<? extends Layer> usedLayers = filterLayers(layers);
-            setVisible(!usedLayers.isEmpty());
-            if (usedLayers.stream().noneMatch(Layer::isVisible)) {
-                slider.setEnabled(false);
-            } else {
-                slider.setEnabled(true);
-                updateSliderWhileEnabled(usedLayers, allHidden);
-            }
-        }
-
-        protected Collection<T> filterLayers(List<Layer> layers) {
-            return Utils.filteredCollection(layers, layerClassFilter);
-        }
-
-        protected abstract void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden);
 
         @Override
@@ -361,10 +335,10 @@
      * @see Layer#setOpacity(double)
      */
-    class OpacitySlider extends AbstractFilterSlider<Layer> {
+    class OpacitySlider extends AbstractFilterSlider {
         /**
          * Create a new {@link OpacitySlider}.
          */
         OpacitySlider() {
-            super(0, 1, DEFAULT_OPACITY, Layer.class);
+            super(0, 1, DEFAULT_OPACITY);
             setLabels("0%", "50%", "100%");
             slider.setToolTipText(tr("Adjust opacity of the layer.") + " " + tr("Double click to reset."));
@@ -376,5 +350,7 @@
                 setVisibleFlag(false);
             } else {
-                super.onStateChanged();
+                for (Layer layer : layerSupplier.get()) {
+                    layer.setOpacity(getRealValue());
+                }
             }
         }
@@ -382,5 +358,5 @@
         @Override
         protected void mouseWheelMoved(MouseWheelEvent e) {
-            if (!isEnabled() && !filterLayers(model.getSelectedLayers()).isEmpty() && e.getPreciseWheelRotation() < 0) {
+            if (!isEnabled() && !layerSupplier.get().isEmpty() && e.getPreciseWheelRotation() < 0) {
                 // make layer visible and set the value.
                 // this allows users to use the mouse wheel to make the layer visible if it was hidden previously.
@@ -393,10 +369,12 @@
 
         @Override
-        protected void applyValueToLayer(Layer layer) {
-            layer.setOpacity(getRealValue());
-        }
-
-        @Override
-        protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
+        public void updateLayers() {
+            Collection<Layer> usedLayers = layerSupplier.get();
+            setVisible(!usedLayers.isEmpty());
+            if (usedLayers.stream().noneMatch(Layer::isVisible)) {
+                slider.setEnabled(false);
+                return;
+            }
+            slider.setEnabled(true);
             double opacity = usedLayers.stream()
                     .mapToDouble(Layer::getOpacity)
@@ -432,5 +410,5 @@
      * @see ImageryFilterSettings#setGamma(double)
      */
-    private class GammaFilterSlider extends AbstractFilterSlider<ImageryLayer> {
+    private class GammaFilterSlider extends AbstractFilterSlider {
 
         /**
@@ -438,5 +416,5 @@
          */
         GammaFilterSlider() {
-            super(-1, 1, DEFAULT_GAMMA_VALUE, ImageryLayer.class);
+            super(-1, 1, DEFAULT_GAMMA_VALUE);
             setLabels("0", "1", "∞");
             slider.setToolTipText(tr("Adjust gamma value of the layer.") + " " + tr("Double click to reset."));
@@ -444,12 +422,18 @@
 
         @Override
-        protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
-            double gamma = ((ImageryLayer) usedLayers.iterator().next()).getFilterSettings().getGamma();
-            setRealValue(mapGammaToInterval(gamma));
-        }
-
-        @Override
-        protected void applyValueToLayer(ImageryLayer layer) {
-            layer.getFilterSettings().setGamma(mapIntervalToGamma(getRealValue()));
+        public void updateLayers() {
+            Collection<ImageryFilterSettings> settings = filterSettingsSupplier.get();
+            setVisible(!settings.isEmpty());
+            if (!settings.isEmpty()) {
+                double gamma = settings.iterator().next().getGamma();
+                setRealValue(mapGammaToInterval(gamma));
+            }
+        }
+
+        @Override
+        protected void onStateChanged() {
+            for (ImageryFilterSettings settings : filterSettingsSupplier.get()) {
+                settings.setGamma(mapIntervalToGamma(getRealValue()));
+            }
         }
 
@@ -492,5 +476,5 @@
      * @see ImageryFilterSettings#setSharpenLevel(double)
      */
-    private class SharpnessSlider extends AbstractFilterSlider<ImageryLayer> {
+    private class SharpnessSlider extends AbstractFilterSlider {
 
         /**
@@ -498,5 +482,5 @@
          */
         SharpnessSlider() {
-            super(0, MAX_SHARPNESS_FACTOR, DEFAULT_SHARPNESS_FACTOR, ImageryLayer.class);
+            super(0, MAX_SHARPNESS_FACTOR, DEFAULT_SHARPNESS_FACTOR);
             setLabels(trc("image sharpness", "blurred"), trc("image sharpness", "normal"), trc("image sharpness", "sharp"));
             slider.setToolTipText(tr("Adjust sharpness/blur value of the layer.") + " " + tr("Double click to reset."));
@@ -504,11 +488,17 @@
 
         @Override
-        protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
-            setRealValue(((ImageryLayer) usedLayers.iterator().next()).getFilterSettings().getSharpenLevel());
-        }
-
-        @Override
-        protected void applyValueToLayer(ImageryLayer layer) {
-            layer.getFilterSettings().setSharpenLevel(getRealValue());
+        public void updateLayers() {
+            Collection<ImageryFilterSettings> settings = filterSettingsSupplier.get();
+            setVisible(!settings.isEmpty());
+            if (!settings.isEmpty()) {
+                setRealValue(settings.iterator().next().getSharpenLevel());
+            }
+        }
+
+        @Override
+        protected void onStateChanged() {
+            for (ImageryFilterSettings settings : filterSettingsSupplier.get()) {
+                settings.setSharpenLevel(getRealValue());
+            }
         }
 
@@ -530,5 +520,5 @@
      * @see ImageryFilterSettings#setColorfulness(double)
      */
-    private class ColorfulnessSlider extends AbstractFilterSlider<ImageryLayer> {
+    private class ColorfulnessSlider extends AbstractFilterSlider {
 
         /**
@@ -536,5 +526,5 @@
          */
         ColorfulnessSlider() {
-            super(0, MAX_COLORFUL_FACTOR, DEFAULT_COLORFUL_FACTOR, ImageryLayer.class);
+            super(0, MAX_COLORFUL_FACTOR, DEFAULT_COLORFUL_FACTOR);
             setLabels(trc("image colorfulness", "less"), trc("image colorfulness", "normal"), trc("image colorfulness", "more"));
             slider.setToolTipText(tr("Adjust colorfulness of the layer.") + " " + tr("Double click to reset."));
@@ -542,11 +532,17 @@
 
         @Override
-        protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
-            setRealValue(((ImageryLayer) usedLayers.iterator().next()).getFilterSettings().getColorfulness());
-        }
-
-        @Override
-        protected void applyValueToLayer(ImageryLayer layer) {
-            layer.getFilterSettings().setColorfulness(getRealValue());
+        public void updateLayers() {
+            Collection<ImageryFilterSettings> settings = filterSettingsSupplier.get();
+            setVisible(!settings.isEmpty());
+            if (!settings.isEmpty()) {
+                setRealValue(settings.iterator().next().getColorfulness());
+            }
+        }
+
+        @Override
+        protected void onStateChanged() {
+            for (ImageryFilterSettings settings : filterSettingsSupplier.get()) {
+                settings.setColorfulness(getRealValue());
+            }
         }
 
@@ -566,11 +562,11 @@
      * @author Michael Zangl
      */
-    private static class ColorSelector extends JPanel implements LayerVisibilityMenuEntry {
-
-        private static final Border NORMAL_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2);
-        private static final Border SELECTED_BORDER = BorderFactory.createLineBorder(Color.BLACK, 2);
+    private class ColorSelector extends JPanel implements LayerVisibilityMenuEntry {
+
+        private final Border NORMAL_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2);
+        private final Border SELECTED_BORDER = BorderFactory.createLineBorder(Color.BLACK, 2);
 
         // TODO: Nicer color palette
-        private static final Color[] COLORS = {
+        private final Color[] COLORS = {
                 Color.RED,
                 Color.ORANGE,
@@ -581,10 +577,8 @@
                 Color.GRAY,
         };
-        private final Supplier<List<Layer>> layerSupplier;
         private final HashMap<Color, JPanel> panels = new HashMap<>();
 
-        ColorSelector(Supplier<List<Layer>> layerSupplier) {
+        ColorSelector() {
             super(new GridBagLayout());
-            this.layerSupplier = layerSupplier;
             add(new JLabel(tr("Color")), GBC.eol().insets(24 + 10, 0, 0, 0));
             for (Color color : COLORS) {
@@ -604,5 +598,5 @@
                 @Override
                 public void mouseClicked(MouseEvent e) {
-                    List<Layer> layers = layerSupplier.get();
+                    Collection<Layer> layers = layerSupplier.get();
                     for (Layer l : layers) {
                         if (l instanceof GpxLayer) {
@@ -623,5 +617,6 @@
 
         @Override
-        public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) {
+        public void updateLayers() {
+            Collection<Layer> layers = layerSupplier.get();
             List<Color> colors = layers.stream().filter(l -> l instanceof GpxLayer)
                     .map(l -> ((GpxLayer) l).getColor())
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 17739)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 17740)
@@ -32,4 +32,6 @@
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
 import org.openstreetmap.josm.data.preferences.DoubleProperty;
+import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
+import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings.FilterChangeListener;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
@@ -37,4 +39,5 @@
 import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.ExifReader;
+import org.openstreetmap.josm.tools.ImageProcessor;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
@@ -45,5 +48,5 @@
  * Offers basic mouse interaction (zoom, drag) and on-screen text.
  */
-public class ImageDisplay extends JComponent implements Destroyable, PreferenceChangedListener {
+public class ImageDisplay extends JComponent implements Destroyable, PreferenceChangedListener, FilterChangeListener {
 
     /** The file that is currently displayed */
@@ -51,5 +54,10 @@
 
     /** The image currently displayed */
-    private transient Image image;
+    private transient BufferedImage image;
+
+    /**
+     * Process the image before it is being displayed
+     */
+    private final ImageProcessor imageProcessor;
 
     /** The image currently displayed */
@@ -295,5 +303,5 @@
         @Override
         public void run() {
-            Image img;
+            BufferedImage img;
             try {
                 img = ImageIO.read(file);
@@ -690,4 +698,8 @@
      */
     public ImageDisplay() {
+        this(image -> image);
+    }
+
+    public ImageDisplay(ImageProcessor imageProcessor) {
         addMouseListener(imgMouseListener);
         addMouseWheelListener(imgMouseListener);
@@ -695,4 +707,8 @@
         Config.getPref().addPreferenceChangeListener(this);
         preferenceChanged(null);
+        this.imageProcessor = imageProcessor;
+        if (imageProcessor instanceof ImageryFilterSettings) {
+            ((ImageryFilterSettings) imageProcessor).addFilterChangeListener(this);
+        }
     }
 
@@ -703,4 +719,7 @@
         removeMouseMotionListener(imgMouseListener);
         Config.getPref().removePreferenceChangeListener(this);
+        if (imageProcessor instanceof ImageryFilterSettings) {
+            ((ImageryFilterSettings) imageProcessor).removeFilterChangeListener(this);
+        }
     }
 
@@ -744,7 +763,12 @@
 
     @Override
+    public void filterChanged() {
+        repaint();
+    }
+
+    @Override
     public void paintComponent(Graphics g) {
         ImageEntry entry;
-        Image image;
+        BufferedImage image;
         VisRect visibleRect;
         boolean errorLoading;
@@ -818,4 +842,8 @@
                     r.x = r.y = 0;
                 }
+            }
+
+            if (image != null) {
+                image = imageProcessor.process(image);
             }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 17739)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 17740)
@@ -36,4 +36,5 @@
 import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.gui.dialogs.layer.LayerVisibilityAction;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
@@ -43,4 +44,5 @@
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
+import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
@@ -53,4 +55,6 @@
  */
 public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener, ImageDataUpdateListener {
+
+    private final ImageryFilterSettings imageryFilterSettings = new ImageryFilterSettings();
 
     private final ImageZoomAction imageZoomAction = new ImageZoomAction();
@@ -64,6 +68,8 @@
     private final ImageLastAction imageLastAction = new ImageLastAction();
     private final ImageCopyPathAction imageCopyPathAction = new ImageCopyPathAction();
-
-    private final ImageDisplay imgDisplay = new ImageDisplay();
+    private final LayerVisibilityAction visibilityAction = new LayerVisibilityAction(Collections::emptyList,
+            () -> Collections.singleton(imageryFilterSettings));
+
+    private final ImageDisplay imgDisplay = new ImageDisplay(imageryFilterSettings);
     private boolean centerView;
 
@@ -162,4 +168,6 @@
         buttons.add(Box.createRigidArea(new Dimension(7, 0)));
         buttons.add(btnCopyPath);
+        buttons.add(Box.createRigidArea(new Dimension(7, 0)));
+        buttons.add(new JButton(visibilityAction));
 
         JPanel bottomPane = new JPanel(new GridBagLayout());
Index: trunk/src/org/openstreetmap/josm/gui/layer/imagery/ImageryFilterSettings.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/imagery/ImageryFilterSettings.java	(revision 17739)
+++ trunk/src/org/openstreetmap/josm/gui/layer/imagery/ImageryFilterSettings.java	(revision 17740)
@@ -2,4 +2,5 @@
 package org.openstreetmap.josm.gui.layer.imagery;
 
+import java.awt.image.BufferedImage;
 import java.util.Arrays;
 import java.util.List;
@@ -13,5 +14,5 @@
  * @since 10547
  */
-public class ImageryFilterSettings {
+public class ImageryFilterSettings implements ImageProcessor {
 
     protected GammaImageProcessor gammaImageProcessor = new GammaImageProcessor();
@@ -85,4 +86,12 @@
     }
 
+    @Override
+    public BufferedImage process(BufferedImage image) {
+        for (ImageProcessor processor : getProcessors()) {
+            image = processor.process(image);
+        }
+        return image;
+    }
+
     /**
      * Adds a filter change listener
