### Eclipse Workspace Patch 1.0
#P josm-2
Index: src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 17942)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(working copy)
@@ -31,6 +31,7 @@
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
 import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings.FilterChangeListener;
+import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
 import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener;
@@ -101,6 +102,30 @@
     /** Show a background for the error text (may be hard on eyes) */
     private static final BooleanProperty ERROR_MESSAGE_BACKGROUND = new BooleanProperty("geoimage.message.error.background", false);
 
+    private updateImageThread updateImageThreadInstance;
+
+    private class updateImageThread extends Thread {
+        private boolean restart;
+
+        @Override
+        public void run() {
+            updateProcessedImage();
+            if (restart) {
+                restart = false;
+                run();
+            }
+        }
+
+        public void restart() {
+            restart = true;
+            if (!isAlive()) {
+                restart = false;
+                updateImageThreadInstance = new updateImageThread();
+                updateImageThreadInstance.start();
+            }
+        }
+    }
+
     @Override
     public void preferenceChanged(PreferenceChangeEvent e) {
         if (e == null ||
@@ -650,12 +675,17 @@
 
     @Override
     public void filterChanged() {
-        updateProcessedImage();
-        repaint();
+        if (updateImageThreadInstance != null) {
+            updateImageThreadInstance.restart();
+        } else {
+            updateImageThreadInstance = new updateImageThread();
+            updateImageThreadInstance.start();
+        }
     }
 
     private void updateProcessedImage() {
         processedImage = image == null ? null : imageProcessor.process(image);
+        GuiHelper.runInEDT(() -> repaint());
     }
 
     @Override
Index: src/org/openstreetmap/josm/gui/layer/imagery/ColorfulFilter.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/imagery/ColorfulFilter.java	(revision 17942)
+++ src/org/openstreetmap/josm/gui/layer/imagery/ColorfulFilter.java	(working copy)
@@ -37,58 +37,76 @@
 
     @Override
     public BufferedImage filter(BufferedImage src, BufferedImage dst) {
-        if (src.getWidth() == 0 || src.getHeight() == 0 || src.getType() == BufferedImage.TYPE_CUSTOM) {
+        if (src.getWidth() == 0 || src.getHeight() == 0) {
             return src;
         }
 
-        BufferedImage dest = Optional.ofNullable(dst).orElseGet(() -> createCompatibleDestImage(src, null));
         int type = src.getType();
 
-        if (type == BufferedImage.TYPE_BYTE_INDEXED) {
-            return filterIndexed(src, dest);
-        }
-
-        DataBuffer srcBuffer = src.getRaster().getDataBuffer();
-        DataBuffer destBuffer = dest.getRaster().getDataBuffer();
-        if (!(srcBuffer instanceof DataBufferByte) || !(destBuffer instanceof DataBufferByte)) {
-            Logging.trace("Cannot apply color filter: Images do not use DataBufferByte.");
-            return src;
-        }
-
-        if (type != dest.getType()) {
-            Logging.trace("Cannot apply color filter: Src / Dest differ in type (" + type + '/' + dest.getType() + ')');
-            return src;
-        }
-        int redOffset;
-        int greenOffset;
-        int blueOffset;
-        int alphaOffset = 0;
         switch (type) {
+        case BufferedImage.TYPE_BYTE_INDEXED:
         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:
-            Logging.trace("Cannot apply color filter: Source image is of wrong type (" + type + ").");
-            return src;
+
+            BufferedImage dest = Optional.ofNullable(dst).orElseGet(() -> createCompatibleDestImage(src, null));
+
+            if (type == BufferedImage.TYPE_BYTE_INDEXED) {
+                try {
+                    return filterIndexed(src, dest);
+                } catch (IllegalArgumentException ex) {
+                    Logging.warn(ex);
+                    break;
+                }
+            }
+
+            DataBuffer srcBuffer = src.getRaster().getDataBuffer();
+            DataBuffer destBuffer = dest.getRaster().getDataBuffer();
+            if (!(srcBuffer instanceof DataBufferByte) || !(destBuffer instanceof DataBufferByte)) {
+                Logging.trace("Images do not use DataBufferByte. Filtering RGB values instead.");
+                break;
+            }
+
+            if (type != dest.getType()) {
+                Logging.trace("Src / Dest differ in type (" + type + '/' + dest.getType() + "). Filtering RGB values instead.");
+                break;
+            }
+
+            int redOffset;
+            int greenOffset;
+            int blueOffset;
+            int 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:
+                return doFilterRGB(src);
+            }
+
+            doFilter((DataBufferByte) srcBuffer, (DataBufferByte) destBuffer, redOffset, greenOffset, blueOffset,
+                    alphaOffset, src.getAlphaRaster() != null);
+            return dest;
         }
-        doFilter((DataBufferByte) srcBuffer, (DataBufferByte) destBuffer, redOffset, greenOffset, blueOffset,
-                alphaOffset, src.getAlphaRaster() != null);
-        return dest;
+
+        return doFilterRGB(src);
     }
 
     /**
@@ -160,17 +178,51 @@
         }
     }
 
-    private byte mix(int color, double luminosity) {
+    private BufferedImage doFilterRGB(BufferedImage src) {
+        int w = src.getWidth();
+        int h = src.getHeight();
+
+        int[] arr = src.getRGB(0, 0, w, h, null, 0, w);
+        int argb, a, r, g, b;
+        double luminosity;
+
+        for (int i = 0; i < arr.length; i++) {
+            argb = arr[i];
+            a = (argb >> 24) & 0xff;
+            r = (argb >> 16) & 0xff;
+            g = (argb >> 8) & 0xff;
+            b = argb & 0xff;
+            luminosity = r * LUMINOSITY_RED + g * LUMINOSITY_GREEN + b * LUMINOSITY_BLUE;
+            r = mixInt(r, luminosity);
+            g = mixInt(g, luminosity);
+            b = mixInt(b, luminosity);
+            argb = a;
+            argb = (argb << 8) + r;
+            argb = (argb << 8) + g;
+            argb = (argb << 8) + b;
+            arr[i] = argb;
+        }
+
+        BufferedImage dest = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+        dest.setRGB(0, 0, w, h, arr, 0, w);
+        return dest;
+    }
+
+    private int mixInt(int color, double luminosity) {
         int val = (int) (colorfulness * color + (1 - colorfulness) * luminosity);
         if (val < 0) {
             return 0;
         } else if (val > 0xff) {
-            return (byte) 0xff;
+            return 0xff;
         } else {
-            return (byte) val;
+            return val;
         }
     }
 
+    private byte mix(int color, double luminosity) {
+        return (byte) mixInt(color, luminosity);
+    }
+
     @Override
     public Rectangle2D getBounds2D(BufferedImage src) {
         return new Rectangle(src.getWidth(), src.getHeight());
Index: test/unit/org/openstreetmap/josm/gui/layer/imagery/ColorfulImageProcessorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/layer/imagery/ColorfulImageProcessorTest.java	(revision 17942)
+++ test/unit/org/openstreetmap/josm/gui/layer/imagery/ColorfulImageProcessorTest.java	(working copy)
@@ -97,7 +97,8 @@
                     BufferedImage.TYPE_3BYTE_BGR,
                     BufferedImage.TYPE_4BYTE_ABGR,
                     BufferedImage.TYPE_4BYTE_ABGR_PRE,
-                    BufferedImage.TYPE_BYTE_INDEXED }) {
+                    BufferedImage.TYPE_BYTE_INDEXED,
+                    BufferedImage.TYPE_BYTE_BINARY}) {
                 assertTrue(runProcessing(data, type));
             }
         }
@@ -122,7 +123,7 @@
     }
 
     private BufferedImage createImage(Color color, int type) {
-        BufferedImage image = type == BufferedImage.TYPE_BYTE_INDEXED
+        BufferedImage image = type == BufferedImage.TYPE_BYTE_INDEXED || type == BufferedImage.TYPE_BYTE_BINARY
                 ? new BufferedImage(TEST_IMAGE_SIZE, TEST_IMAGE_SIZE, type, COLOR_MODEL)
                 : new BufferedImage(TEST_IMAGE_SIZE, TEST_IMAGE_SIZE, type);
         Graphics2D graphics = image.createGraphics();
