diff --git a/images/dialogs/layerlist/colorfulness.png b/images/dialogs/layerlist/colorfulness.png
new file mode 100644
index 0000000..490f35a
Binary files /dev/null and b/images/dialogs/layerlist/colorfulness.png differ
diff --git a/images/dialogs/layerlist/colorfulness.xcf b/images/dialogs/layerlist/colorfulness.xcf
new file mode 100644
index 0000000..031cfae
Binary files /dev/null and b/images/dialogs/layerlist/colorfulness.xcf differ
diff --git a/images/dialogs/layerlist/gamma.png b/images/dialogs/layerlist/gamma.png
index f67ada4..3afee11 100644
Binary files a/images/dialogs/layerlist/gamma.png and b/images/dialogs/layerlist/gamma.png differ
diff --git a/images/dialogs/layerlist/gamma.xcf b/images/dialogs/layerlist/gamma.xcf
new file mode 100644
index 0000000..8d8e59f
Binary files /dev/null and b/images/dialogs/layerlist/gamma.xcf differ
diff --git a/images/dialogs/layerlist/sharpness.png b/images/dialogs/layerlist/sharpness.png
new file mode 100644
index 0000000..b381935
Binary files /dev/null and b/images/dialogs/layerlist/sharpness.png differ
diff --git a/images/dialogs/layerlist/sharpness.xcf b/images/dialogs/layerlist/sharpness.xcf
new file mode 100644
index 0000000..3e50dd5
Binary files /dev/null and b/images/dialogs/layerlist/sharpness.xcf differ
diff --git a/images/dialogs/layerlist/transparency.png b/images/dialogs/layerlist/transparency.png
index 3149c9d..0498ca1 100644
Binary files a/images/dialogs/layerlist/transparency.png and b/images/dialogs/layerlist/transparency.png differ
diff --git a/images/dialogs/layerlist/transparency.xcf b/images/dialogs/layerlist/transparency.xcf
new file mode 100644
index 0000000..906a920
Binary files /dev/null and b/images/dialogs/layerlist/transparency.xcf differ
diff --git a/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java b/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
index 41cad51..82901e9 100644
--- a/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
+++ b/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
@@ -531,12 +531,14 @@ public class LayerListDialog extends ToggleDialog {
     public static final class LayerVisibilityAction extends AbstractAction implements IEnabledStateUpdating, LayerAction {
         protected static final int SLIDER_STEPS = 100;
         private static final double MAX_GAMMA_FACTOR = 2;
+        private static final double MAX_SHARPNESS_FACTOR = 2;
+        private static final double MAX_COLORFUL_FACTOR = 2;
         private final LayerListModel model;
         private final JPopupMenu popup;
-        private JSlider opacitySlider;
-        private JSlider gammaSlider;
         private SideButton sideButton;
         private JCheckBox visibilityCheckbox;
+        final OpacitySlider opacitySlider = new OpacitySlider();
+        private final ArrayList<FilterSlider<?>> sliders = new ArrayList<>();
 
         /**
          * Creates a new {@link LayerVisibilityAction}
@@ -559,69 +561,31 @@ public class LayerListDialog extends ToggleDialog {
             visibilityCheckbox.addChangeListener(new ChangeListener() {
                 @Override
                 public void stateChanged(ChangeEvent e) {
-                    setVisible(visibilityCheckbox.isSelected());
+                    setVisibleFlag(visibilityCheckbox.isSelected());
                 }
             });
             content.add(visibilityCheckbox, GBC.eop());
 
-            content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "transparency")), GBC.std().span(1, 2).insets(0, 0, 5, 0));
-            content.add(new JLabel(tr("Opacity")), GBC.eol());
-            opacitySlider = new JSlider(JSlider.HORIZONTAL);
-            opacitySlider.setMaximum(SLIDER_STEPS);
-            opacitySlider.addChangeListener(new ChangeListener() {
-                @Override
-                public void stateChanged(ChangeEvent e) {
-                    setOpacityValue(readOpacityValue(), opacitySlider.getValueIsAdjusting());
-                }
-            });
-            opacitySlider.setToolTipText(tr("Adjust opacity of the layer."));
-            content.add(opacitySlider, GBC.eop());
-
-            content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "gamma")), GBC.std().span(1, 2).insets(0, 0, 5, 0));
-            content.add(new JLabel(tr("Gamma")), GBC.eol());
-            gammaSlider = new JSlider(JSlider.HORIZONTAL);
-            gammaSlider.setMaximum(SLIDER_STEPS);
-            gammaSlider.addChangeListener(new ChangeListener() {
-                @Override
-                public void stateChanged(ChangeEvent e) {
-                    setGammaValue(readGammaValue());
-                }
-            });
-            gammaSlider.setToolTipText(tr("Adjust gamma value of the layer."));
-            content.add(gammaSlider, GBC.eol());
-        }
-
-        protected double readOpacityValue() {
-            return (double) opacitySlider.getValue() / SLIDER_STEPS;
+            addSlider(content, opacitySlider);
+            addSlider(content, new ColorfulnessSlider());
+            addSlider(content, new GammaFilterSlider());
+            addSlider(content, new SharpnessSlider());
         }
 
-        protected double readGammaValue() {
-            return (double) gammaSlider.getValue() / SLIDER_STEPS * MAX_GAMMA_FACTOR;
+        private void addSlider(JPanel content, FilterSlider<?> slider) {
+            content.add(new JLabel(slider.getIcon()), GBC.std().span(1, 2).insets(0, 0, 5, 0));
+            content.add(new JLabel(slider.getLabel()), GBC.eol());
+            content.add(slider, GBC.eop());
+            sliders.add(slider);
         }
 
-        protected void setVisible(boolean visible) {
+        protected void setVisibleFlag(boolean visible) {
             for (Layer l : model.getSelectedLayers()) {
                 l.setVisible(visible);
             }
             updateValues();
         }
 
-        protected void setOpacityValue(double value, boolean adjusting) {
-            if (value <= 0 && !adjusting) {
-                setVisible(false);
-            } else {
-                for (Layer l : model.getSelectedLayers()) {
-                    l.setOpacity(value);
-                }
-            }
-        }
-
-        protected void setGammaValue(double value) {
-            for (ImageryLayer imageryLayer : Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class)) {
-                imageryLayer.setGamma(value);
-            }
-        }
-
         @Override
         public void actionPerformed(ActionEvent e) {
             updateValues();
@@ -647,37 +611,8 @@ public class LayerListDialog extends ToggleDialog {
             // TODO: Indicate tristate.
             visibilityCheckbox.setSelected(allVisible && !allHidden);
 
-            updateOpacitySlider(layers, allHidden);
-
-            updateGammaSlider(layers, allHidden);
-        }
-
-        private void updateGammaSlider(List<Layer> layers, boolean allHidden) {
-            Collection<ImageryLayer> gammaLayers = Utils.filteredCollection(layers, ImageryLayer.class);
-            if (gammaLayers.isEmpty() || allHidden) {
-                gammaSlider.setEnabled(false);
-            } else {
-                gammaSlider.setEnabled(true);
-                double gamma = gammaLayers.iterator().next().getGamma();
-                gammaSlider.setValue((int) (gamma * SLIDER_STEPS / MAX_GAMMA_FACTOR));
-            }
-        }
-
-        private void updateOpacitySlider(List<Layer> layers, boolean allHidden) {
-            if (layers.isEmpty() || allHidden) {
-                opacitySlider.setEnabled(false);
-            } else {
-                opacitySlider.setEnabled(true);
-                double opacity = 0;
-                for (Layer l : layers) {
-                    opacity += l.getOpacity();
-                }
-                opacity /= layers.size();
-                if (opacity == 0) {
-                    opacity = 1;
-                    setOpacityValue(opacity, false);
-                }
-                opacitySlider.setValue((int) (opacity * SLIDER_STEPS));
+            for (FilterSlider<?> slider : sliders) {
+                slider.updateSlider(layers, allHidden);
             }
         }
 
@@ -703,6 +638,274 @@ public class LayerListDialog extends ToggleDialog {
         void setCorrespondingSideButton(SideButton sideButton) {
             this.sideButton = sideButton;
         }
+
+        /**
+         * This is a slider for a filter value.
+         * @author Michael Zangl
+         *
+         * @param <T> The layer type.
+         */
+        private abstract class FilterSlider<T extends Layer> extends JSlider {
+            private final double minValue;
+            private final double maxValue;
+            private final Class<T> layerClassFilter;
+
+            /**
+             * Create a new filter slider.
+             * @param minValue The minimum value to map to the left side.
+             * @param maxValue The maximum value to map to the right side.
+             * @param layerClassFilter The type of layer influenced by this filter.
+             */
+            FilterSlider(double minValue, double maxValue, Class<T> layerClassFilter) {
+                super(JSlider.HORIZONTAL);
+                this.minValue = minValue;
+                this.maxValue = maxValue;
+                this.layerClassFilter = layerClassFilter;
+                setMaximum(SLIDER_STEPS);
+                int tick = convertFromRealValue(1);
+                setMinorTickSpacing(tick);
+                setMajorTickSpacing(tick);
+                setPaintTicks(true);
+
+                addChangeListener(new ChangeListener() {
+                    @Override
+                    public void stateChanged(ChangeEvent e) {
+                        onStateChanged();
+                    }
+                });
+            }
+
+            /**
+             * Called whenever the state of the slider was changed.
+             * @see #getValueIsAdjusting()
+             * @see #getRealValue()
+             */
+            protected void onStateChanged() {
+                Collection<T> layers = filterLayers(model.getSelectedLayers());
+                for (T layer : layers) {
+                    applyValueToLayer(layer);
+                }
+            }
+
+            protected void applyValueToLayer(T layer) {
+            }
+
+            protected double getRealValue() {
+                return convertToRealValue(getValue());
+            }
+
+            protected double convertToRealValue(int value) {
+                double s = (double) value / SLIDER_STEPS;
+                return s * maxValue + (1-s) * minValue;
+            }
+
+            protected void setRealValue(double value) {
+                setValue(convertFromRealValue(value));
+            }
+
+            protected int convertFromRealValue(double value) {
+                int i = (int) ((value - minValue) / (maxValue - minValue) * SLIDER_STEPS + .5);
+                if (i < getMinimum()) {
+                    return getMinimum();
+                } else if (i > getMaximum()) {
+                    return getMaximum();
+                } else {
+                    return i;
+                }
+            }
+
+            public abstract ImageIcon getIcon();
+
+            public abstract String getLabel();
+
+            public void updateSlider(List<Layer> layers, boolean allHidden) {
+                Collection<? extends Layer> usedLayers = filterLayers(layers);
+                if (usedLayers.isEmpty() || allHidden) {
+                    setEnabled(false);
+                } else {
+                    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);
+        }
+
+        /**
+         * This slider allows you to change the opacity of a layer.
+         *
+         * @author Michael Zangl
+         * @see Layer#setOpacity(double)
+         */
+        class OpacitySlider extends FilterSlider<Layer> {
+            /**
+             * Creaate a new {@link OpacitySlider}.
+             */
+            OpacitySlider() {
+                super(0, 1, Layer.class);
+                setToolTipText(tr("Adjust opacity of the layer."));
+
+            }
+
+            @Override
+            protected void onStateChanged() {
+                if (getRealValue() <= 0.001 && !getValueIsAdjusting()) {
+                    setVisibleFlag(false);
+                } else {
+                    super.onStateChanged();
+                }
+            }
+
+            @Override
+            protected void applyValueToLayer(Layer layer) {
+                layer.setOpacity(getRealValue());
+            }
+
+            @Override
+            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
+                double opacity = 0;
+                for (Layer l : usedLayers) {
+                    opacity += l.getOpacity();
+                }
+                opacity /= usedLayers.size();
+                if (opacity == 0) {
+                    opacity = 1;
+                    setVisibleFlag(true);
+                }
+                setRealValue(opacity);
+            }
+
+            @Override
+            public String getLabel() {
+                return tr("Opacity");
+            }
+
+            @Override
+            public ImageIcon getIcon() {
+                return ImageProvider.get("dialogs/layerlist", "transparency");
+            }
+
+            @Override
+            public String toString() {
+                return "OpacitySlider [getRealValue()=" + getRealValue() + "]";
+            }
+        }
+
+        /**
+         * This slider allows you to change the gamma value of a layer.
+         *
+         * @author Michael Zangl
+         * @see ImageryLayer#setGamma(double)
+         */
+        private class GammaFilterSlider extends FilterSlider<ImageryLayer> {
+
+            /**
+             * Create a new {@link GammaFilterSlider}
+             */
+            GammaFilterSlider() {
+                super(0, MAX_GAMMA_FACTOR, ImageryLayer.class);
+                setToolTipText(tr("Adjust gamma value of the layer."));
+            }
+
+            @Override
+            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
+                double gamma = ((ImageryLayer) usedLayers.iterator().next()).getGamma();
+                setRealValue(gamma);
+            }
+
+            @Override
+            protected void applyValueToLayer(ImageryLayer layer) {
+                layer.setGamma(getRealValue());
+            }
+
+            @Override
+            public ImageIcon getIcon() {
+               return ImageProvider.get("dialogs/layerlist", "gamma");
+            }
+
+            @Override
+            public String getLabel() {
+                return tr("Gamma");
+            }
+        }
+
+        /**
+         * This slider allows you to change the sharpness of a layer.
+         *
+         * @author Michael Zangl
+         * @see ImageryLayer#setSharpenLevel(double)
+         */
+        private class SharpnessSlider extends FilterSlider<ImageryLayer> {
+
+            /**
+             * Creates a new {@link SharpnessSlider}
+             */
+            SharpnessSlider() {
+                super(0, MAX_SHARPNESS_FACTOR, ImageryLayer.class);
+                setToolTipText(tr("Adjust sharpness/blur value of the layer."));
+            }
+
+            @Override
+            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
+                setRealValue(((ImageryLayer) usedLayers.iterator().next()).getSharpenLevel());
+            }
+
+            @Override
+            protected void applyValueToLayer(ImageryLayer layer) {
+                layer.setSharpenLevel(getRealValue());
+            }
+
+            @Override
+            public ImageIcon getIcon() {
+               return ImageProvider.get("dialogs/layerlist", "sharpness");
+            }
+
+            @Override
+            public String getLabel() {
+                return tr("Sharpness");
+            }
+        }
+
+        /**
+         * This slider allows you to change the colorfulness of a layer.
+         *
+         * @author Michael Zangl
+         * @see ImageryLayer#setColorfulness(double)
+         */
+        private class ColorfulnessSlider extends FilterSlider<ImageryLayer> {
+
+            /**
+             * Create a new {@link ColorfulnessSlider}
+             */
+            ColorfulnessSlider() {
+                super(0, MAX_COLORFUL_FACTOR, ImageryLayer.class);
+                setToolTipText(tr("Adjust colorfulness of the layer."));
+            }
+
+            @Override
+            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
+                setRealValue(((ImageryLayer) usedLayers.iterator().next()).getColorfulness());
+            }
+
+            @Override
+            protected void applyValueToLayer(ImageryLayer layer) {
+                layer.setColorfulness(getRealValue());
+            }
+
+            @Override
+            public ImageIcon getIcon() {
+               return ImageProvider.get("dialogs/layerlist", "colorfulness");
+            }
+
+            @Override
+            public String getLabel() {
+                return tr("Colorfulness");
+            }
+        }
     }
 
     /**
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
index b52d9df..6467fe7 100644
--- a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
@@ -258,6 +258,18 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener {
         redraw();
     }
 
+    @Override
+    public void setSharpenLevel(double sharpenLevel) {
+        super.setSharpenLevel(sharpenLevel);
+        redraw();
+    }
+
+    @Override
+    public void setColorfulness(double colorfulness) {
+        super.setColorfulness(colorfulness);
+        redraw();
+    }
+
     /**
      * Marks layer as needing redraw on offset change
      */
diff --git a/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java b/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
index d1eb563..5b3f7a7 100644
--- a/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
@@ -8,11 +8,18 @@ import static org.openstreetmap.josm.tools.I18n.trc;
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.GridBagLayout;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
 import java.awt.Transparency;
 import java.awt.event.ActionEvent;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.BufferedImageOp;
+import java.awt.image.ColorModel;
 import java.awt.image.ConvolveOp;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
 import java.awt.image.Kernel;
 import java.awt.image.LookupOp;
 import java.awt.image.ShortLookupTable;
@@ -69,6 +76,8 @@ public abstract class ImageryLayer extends Layer {
     protected double dy;
 
     protected GammaImageProcessor gammaImageProcessor = new GammaImageProcessor();
+    protected SharpenImageProcessor sharpenImageProcessor = new SharpenImageProcessor();
+    protected ColorfulImageProcessor collorfulnessImageProcessor = new ColorfulImageProcessor();
 
     private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this);
 
@@ -86,8 +95,10 @@ public abstract class ImageryLayer extends Layer {
         if (icon == null) {
             icon = ImageProvider.get("imagery_small");
         }
-        addImageProcessor(createSharpener(PROP_SHARPEN_LEVEL.get()));
+        addImageProcessor(collorfulnessImageProcessor);
         addImageProcessor(gammaImageProcessor);
+        addImageProcessor(sharpenImageProcessor);
+        sharpenImageProcessor.setSharpenLevel(1 + PROP_SHARPEN_LEVEL.get() / 2f);
     }
 
     public double getPPD() {
@@ -232,6 +243,8 @@ public abstract class ImageryLayer extends Layer {
         return hasBookmarks ? subMenu : adjustMenuItem;
     }
 
+    // Can soon be removed. Replaced by sharpen image processor.
+    @Deprecated
     public ImageProcessor createSharpener(int sharpenLevel) {
         final Kernel kernel;
         if (sharpenLevel == 1) {
@@ -300,6 +313,258 @@ public abstract class ImageryLayer extends Layer {
     }
 
     /**
+     * Sharpens or blurs the image, depending on the sharpen value.
+     * <p>
+     * A positive sharpen level means that we sharpen the image.
+     * <p>
+     * A negative sharpen level let's us blur the image. -1 is the most useful value there.
+     *
+     * @author Michael Zangl
+     */
+    public static class SharpenImageProcessor implements ImageProcessor {
+        private float sharpenLevel = 0;
+        private ConvolveOp op;
+
+        private static float[] KERNEL_IDENTITY = new float[] {
+            0, 0, 0,
+            0, 1, 0,
+            0, 0, 0
+        };
+
+        private static float[] KERNEL_BLUR = new float[] {
+            1f / 16, 2f / 16, 1f / 16,
+            2f / 16, 4f / 16, 2f / 16,
+            1f / 16, 2f / 16, 1f / 16
+        };
+
+        private static float[] KERNEL_SHARPEN = new float[] {
+            -.5f, -1f, -.5f,
+             -1f,  7,  -1f,
+            -.5f, -1f, -.5f
+        };
+
+        /**
+         * Gets the current sharpen level.
+         * @return The level.
+         */
+        public float getSharpenLevel() {
+            return sharpenLevel;
+        }
+
+        /**
+         * Sets the sharpening level.
+         * @param sharpenLevel The level. Clamped to be positive or 0.
+         */
+        public void setSharpenLevel(float sharpenLevel) {
+            if (sharpenLevel < 0) {
+                this.sharpenLevel = 0;
+            } else {
+                this.sharpenLevel = sharpenLevel;
+            }
+
+            if (this.sharpenLevel < 0.95) {
+                op = generateMixed(this.sharpenLevel, KERNEL_IDENTITY, KERNEL_BLUR);
+            } else if (this.sharpenLevel > 1.05) {
+                op = generateMixed(this.sharpenLevel - 1, KERNEL_SHARPEN, KERNEL_IDENTITY);
+            } else {
+                op = null;
+            }
+        }
+
+        private ConvolveOp generateMixed(float aFactor, float[] a, float[] b) {
+            if (a.length != 9 || b.length != 9) {
+                throw new IllegalArgumentException("Illegal kernel array length.");
+            }
+            float[] values = new float[9];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = aFactor * a[i] + (1 - aFactor) * b[i];
+            }
+            return new ConvolveOp(new Kernel(3, 3, values), ConvolveOp.EDGE_NO_OP, null);
+        }
+
+        @Override
+        public BufferedImage process(BufferedImage image) {
+            if (op != null) {
+                return op.filter(image, null);
+            } else {
+                return image;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SharpenImageProcessor [sharpenLevel=" + sharpenLevel + "]";
+        }
+    }
+
+    /**
+     * Adds or removes the colorfulness of the image.
+     *
+     * @author Michael Zangl
+     */
+    public static class ColorfulImageProcessor implements ImageProcessor {
+        private ColorfulFilter op = null;
+        private double colorfulness = 1;
+
+        /**
+         * Gets the colorfulness value.
+         * @return The value
+         */
+        public double getColorfulness() {
+            return colorfulness;
+        }
+
+        /**
+         * Sets the colorfulness value. Clamps it to 0+
+         * @param colorfulness The value
+         */
+        public void setColorfulness(double colorfulness) {
+            if (colorfulness < 0) {
+                this.colorfulness = 0;
+            } else {
+                this.colorfulness = colorfulness;
+            }
+
+            if (this.colorfulness < .95 || this.colorfulness > 1.05) {
+                op = new ColorfulFilter(this.colorfulness);
+            } else {
+                op = null;
+            }
+        }
+
+        @Override
+        public BufferedImage process(BufferedImage image) {
+            if (op != null) {
+                return op.filter(image, null);
+            } else {
+                return image;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "ColorfulImageProcessor [colorfulness=" + colorfulness + "]";
+        }
+    }
+
+    private static class ColorfulFilter implements BufferedImageOp {
+        private final double colorfulness;
+
+        /**
+         * Create a new colorful filter.
+         * @param colorfulness The colorfulness as defined in the {@link ColorfulImageProcessor} class.
+         */
+        ColorfulFilter(double colorfulness) {
+            this.colorfulness = colorfulness;
+        }
+
+        @Override
+        public BufferedImage filter(BufferedImage src, BufferedImage dest) {
+            if (src.getWidth() == 0 || src.getHeight() == 0) {
+                return src;
+            }
+
+            if (dest == null) {
+                dest = createCompatibleDestImage(src, null);
+            }
+            DataBuffer srcBuffer = src.getRaster().getDataBuffer();
+            DataBuffer destBuffer = dest.getRaster().getDataBuffer();
+            if (!(srcBuffer instanceof DataBufferByte) || !(destBuffer instanceof DataBufferByte)) {
+                Main.trace("Cannot apply color filter: Images do not use DataBufferByte.");
+                return src;
+            }
+
+            int type = src.getType();
+            if (type != dest.getType()) {
+                Main.trace("Cannot apply color filter: Src / Dest differ in type (" + type + "/" + dest.getType() + ")");
+                return src;
+            }
+            int redOffset, greenOffset, blueOffset, alphaOffset = 0;
+            switch (type) {
+            case BufferedImage.TYPE_3BYTE_BGR:
+                blueOffset = 0;
+                greenOffset = 1;
+                redOffset = 2;
+                break;
+            case BufferedImage.TYPE_4BYTE_ABGR:
+            case BufferedImage.TYPE_4BYTE_ABGR_PRE:
+                blueOffset = 1;
+                greenOffset = 2;
+                redOffset = 3;
+                break;
+            case BufferedImage.TYPE_INT_ARGB:
+            case BufferedImage.TYPE_INT_ARGB_PRE:
+                redOffset = 0;
+                greenOffset = 1;
+                blueOffset = 2;
+                alphaOffset = 3;
+                break;
+            default:
+                Main.trace("Cannot apply color filter: Source image is of wrong type (" + type + ").");
+                return src;
+            }
+            doFilter((DataBufferByte) srcBuffer, (DataBufferByte) destBuffer, redOffset, greenOffset, blueOffset,
+                    alphaOffset, src.getAlphaRaster() != null);
+            return dest;
+        }
+
+        private void doFilter(DataBufferByte src, DataBufferByte dest, int redOffset, int greenOffset, int blueOffset,
+                int alphaOffset, boolean hasAlpha) {
+            byte[] srcPixels = src.getData();
+            byte[] destPixels = dest.getData();
+            if (srcPixels.length != destPixels.length) {
+                Main.trace("Cannot apply color filter: Source/Dest lengths differ.");
+                return;
+            }
+            int entries = hasAlpha ? 4 : 3;
+            for (int i = 0; i < srcPixels.length; i += entries) {
+                int r = srcPixels[i + redOffset] & 0xff;
+                int g = srcPixels[i + greenOffset] & 0xff;
+                int b = srcPixels[i + blueOffset] & 0xff;
+                float luminosity = r * .21f + g * .72f + b * .07f;
+                destPixels[i + redOffset] = mix(r, luminosity);
+                destPixels[i + greenOffset] = mix(g, luminosity);
+                destPixels[i + blueOffset] = mix(b, luminosity);
+                if (hasAlpha) {
+                    destPixels[i + alphaOffset] = srcPixels[i + alphaOffset];
+                }
+            }
+        }
+
+        private byte mix(int color, float luminosity) {
+            int val = (int) (colorfulness * color +  (1 - colorfulness) * luminosity);
+            if (val < 0) {
+                return 0;
+            } else if (val > 0xff) {
+                return (byte) 0xff;
+            } else {
+                return (byte) val;
+            }
+        }
+
+        @Override
+        public Rectangle2D getBounds2D(BufferedImage src) {
+            return new Rectangle(src.getWidth(), src.getHeight());
+        }
+
+        @Override
+        public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) {
+            return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
+        }
+
+        @Override
+        public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
+            return (Point2D) srcPt.clone();
+        }
+
+        @Override
+        public RenderingHints getRenderingHints() {
+            return null;
+        }
+
+    }
+
+    /**
      * Returns the currently set gamma value.
      * @return the currently set gamma value
      */
@@ -316,6 +581,44 @@ public abstract class ImageryLayer extends Layer {
     }
 
     /**
+     * Gets the current sharpen level.
+     * @return The sharpen level.
+     */
+    public double getSharpenLevel() {
+        return sharpenImageProcessor.getSharpenLevel();
+    }
+
+    /**
+     * Sets the sharpen level for the layer.
+     * <code>1</code> means no change in sharpness.
+     * Values in range 0..1 blur the image.
+     * Values above 1 are used to sharpen the image.
+     * @param sharpenLevel The sharpen level.
+     */
+    public void setSharpenLevel(double sharpenLevel) {
+        sharpenImageProcessor.setSharpenLevel((float) sharpenLevel);
+    }
+
+    /**
+     * Gets the colorfulness of this image.
+     * @return The colorfulness
+     */
+    public double getColorfulness() {
+        return collorfulnessImageProcessor.getColorfulness();
+    }
+
+    /**
+     * Sets the colorfulness of this image.
+     * 0 means grayscale.
+     * 1 means normal colorfulness.
+     * Values greater than 1 are allowed.
+     * @param colorfulness The colorfulness.
+     */
+    public void setColorfulness(double colorfulness) {
+        collorfulnessImageProcessor.setColorfulness(colorfulness);
+    }
+
+    /**
      * This method adds the {@link ImageProcessor} to this Layer if it is not {@code null}.
      *
      * @param processor that processes the image
diff --git a/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java b/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java
index afede87..9157742 100644
--- a/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java
+++ b/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java
@@ -46,32 +46,31 @@ public class LayerListDialogTest {
 
             // now check values
             action.updateValues();
-            assertEquals(1.0, action.readOpacityValue(), 1e-15);
-            assertEquals(1.0, action.readGammaValue(), 1e-15);
+            assertEquals(1.0, action.opacitySlider.getRealValue(), 1e-15);
 
-            action.setOpacityValue(.5, false);
-            action.setGammaValue(1.5);
+            action.opacitySlider.setRealValue(.5);
             action.updateValues();
 
-            assertEquals(0.5, action.readOpacityValue(), 1e-15);
-            assertEquals(1.5, action.readGammaValue(), 1e-15);
+            assertEquals(0.5, action.opacitySlider.getRealValue(), 1e-15);
 
-            action.setVisible(false);
+            action.setVisibleFlag(false);
             action.updateValues();
             assertFalse(layer.isVisible());
 
-            action.setVisible(true);
+            action.setVisibleFlag(true);
             action.updateValues();
             assertTrue(layer.isVisible());
 
             // layer stays visible during adjust
-            action.setOpacityValue(0, true);
+            action.opacitySlider.setValueIsAdjusting(true);
+            action.opacitySlider.setRealValue(0);
             assertEquals(0, layer.getOpacity(), 1e-15);
             layer.setOpacity(.1); // to make layer.isVisible work
             assertTrue(layer.isVisible());
             layer.setOpacity(0);
 
-            action.setOpacityValue(0, false);
+            action.opacitySlider.setValueIsAdjusting(false);
+            action.opacitySlider.setRealValue(0);
             assertEquals(0, layer.getOpacity(), 1e-15);
             layer.setOpacity(.1); // to make layer.isVisible work
             assertFalse(layer.isVisible());
@@ -79,9 +78,9 @@ public class LayerListDialogTest {
             action.updateValues();
 
             // Opacity reset when it was 0 and user set layer to visible.
-            action.setVisible(true);
+            action.setVisibleFlag(true);
             action.updateValues();
-            assertEquals(1.0, action.readOpacityValue(), 1e-15);
+            assertEquals(1.0, action.opacitySlider.getRealValue(), 1e-15);
             assertEquals(1.0, layer.getOpacity(), 1e-15);
 
         } finally {
